Stringa "true" e "false" in booleano


85

Ho un'applicazione Rails e sto usando jQuery per interrogare la mia vista di ricerca in background. Sono presenti campi q(termine di ricerca) start_date, end_datee internal. Il internalcampo è una casella di controllo e sto utilizzando il is(:checked)metodo per creare l'URL che viene richiesto:

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

Ora il mio problema è params[:internal]perché c'è una stringa che contiene "true" o "false" e ho bisogno di lanciarla in booleano. Certo che posso farlo in questo modo:

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

Ma penso che ci debba essere un modo più rubino per affrontare questo problema! Non c'è ...?

Risposte:


133

Per quanto ne so, non esiste un modo predefinito per lanciare stringhe in booleani, ma se le tue stringhe consistessero solo 'true'e 'false'potresti abbreviare il tuo metodo come segue:

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

8
solo una piccola modifica str == 'true' || str = '1'
AM Tourky

30
forse str.downcase == "true" per completezza
JEMaddux

@AMTourky non dovrebbe essere str == 'true' || str == "1" con due "=="?
Pascal

@Lowryder Sì! se si utilizza la casella di controllo predefinita.
7urkm3 del

49

ActiveRecord fornisce un modo pulito per farlo.

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

ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES ha tutte le rappresentazioni ovvie dei valori True come stringhe.


16
Ancora più semplice, usa ActiveRecord::ConnectionAdapters::Column.value_to_boolean(string)(fonte) apidock.com/rails/v3.0.9/ActiveRecord/ConnectionAdapters/Column/…
Mike Atlas

Sì, nelle ultime versioni!
Satya Kalluri

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

L'elenco dei valori falsi è stato spostato in ActiveModel :: Type :: Boolean in Rails 5
divideByZero

ActiveModel::Type::Booleansembra un percorso molto più adatto - sebbene ActiveRecord::ConnectionAdapters::Column::TRUE_VALUEScontenga valori "veritieri", si potrebbe sostenere che è solo incidentalmente il caso e che i valori che dovrebbero essere considerati veritieri per quel caso d'uso specifico ma non altri potrebbero essere inclusi. D'altra parte, ActiveModel::Type::Booleanè apparentemente progettato per essere utilizzato in modo generico.
Lyndsy Simon

24

Avviso di sicurezza

Si noti che questa risposta nella sua forma nuda è appropriata solo per l'altro caso d'uso elencato di seguito piuttosto che per quello nella domanda. Sebbene per lo più risolte, sono state riscontrate numerose vulnerabilità di sicurezza relative a YAML causate dal caricamento dell'input dell'utente come YAML.


Un trucco che uso per convertire le stringhe in bool è YAML.load, ad esempio:

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

YAML bool accetta un bel po 'di stringhe true / false:

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

Un altro caso d'uso

Supponi di avere un pezzo di codice di configurazione come questo:

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

E nella riga di comando:

$ export ETC_SOMETHING=false

Poiché le ENVvariabili sono stringhe una volta all'interno del codice, config.etc.somethingil valore di sarebbe la stringa "false"e verrebbe erroneamente valutato true. Ma se ti piace questo:

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

andrebbe tutto bene. Questo è compatibile anche con il caricamento delle configurazioni da file .yml.


1
Va bene se la stringa passata è sotto il tuo controllo. In questo caso, i valori forniti provengono dal browser dell'utente e come tali dovrebbero essere considerati non sicuri. YAML ti consente di serializzare / deserializzare qualsiasi oggetto Ruby e questo è potenzialmente pericoloso. Ci sono stati numerosi incidenti: google.com/webhp?q=rails+yaml+vulnerability
Teoulas

1
@Teoulas, sono completamente d'accordo con te. In effetti, sto aggiungendo un avviso in modo che le persone non lo usino in modo insicuro.
Halil Özgür

16

Non esiste alcun modo integrato per gestirlo (sebbene actionpack potrebbe avere un aiuto per questo). Consiglierei qualcosa di simile

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

Ciò che funziona è anche usare 0 e non 0 invece di valori letterali falsi / veri:

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

3
non hai bisogno della guardia "se ..." se usi "!! (s = ~ / regex_here /)" perché "nil = ~ / anything /" restituisce zero.
Pavling

Ah davvero. L'ho aggiunto ma ho mantenuto anche il vecchio, perché penso che .matchsia un po 'più facile da leggere.
Marcel Jackwerth

7

ActiveRecord::Type::Boolean.new.type_cast_from_userlo fa secondo le mappature interne di 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

Quindi potresti creare il tuo metodo to_b(o to_boolo to_boolean) in un inizializzatore come questo:

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

2
È ActiveRecord :: Type :: Boolean.new.cast (value) in Rails 5 (vedi CWitty sotto)
Dave Burt


6

In Rails 5 puoi usarlo ActiveRecord::Type::Boolean.new.cast(value)per lanciarlo in un booleano.


1
attenzione però, ActiveRecord :: Type :: Boolean.new.cast ("42") restituisce true
divideByZero

3

Non credo che nulla di simile sia integrato in Ruby. Puoi riaprire la classe String e aggiungere il metodo to_bool lì:

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

Quindi puoi usarlo ovunque nel tuo progetto, in questo modo: params[:internal].to_bool


2
Sicuramente non vorrei avere una to_boolfunzione di ritorno nil; sembra sbagliato. Altre funzioni di conversione non lo fanno: "a".to_irestituisce 0, nonnil
Krease

3

Forse str.to_s.downcase == 'true'per completezza. Quindi nulla può bloccarsi anche se strè zero o 0.


2

Guardando il codice sorgente di Virtus , forse farei qualcosa del genere:

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

Potresti considerare di aggiungere internalal tuo URL solo se è vero, quindi se la casella di controllo non è selezionata e non lo aggiungi params[:internal]sarebbenil , il che restituisce falso in Ruby.

Non ho molta familiarità con lo specifico jQuery che stai utilizzando, ma esiste un modo più pulito per chiamare ciò che desideri rispetto alla creazione manuale di una stringa URL? Hai dato un'occhiata a $gete $ajax?


1

Potresti aggiungere alla classe String per avere il metodo to_boolean. Quindi potresti fare 'true'.to_boolean o' 1'.to_boolean

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

-5

Sono sorpreso che nessuno abbia pubblicato questa semplice soluzione. Cioè se le tue stringhe saranno "vere" o "false".

def to_boolean(str)
    eval(str)
end

4
Questo perché questa soluzione è un disastro di sicurezza. : D
davidb

1
Il problema con questa soluzione è l'input dell'utente: se qualcuno digita to_boolean("ActiveRecord::Base.connection.execute('DROP TABLE *')"), distruggerà il tuo database (e restituirà true!). Buon divertimento: D
Ben Aubin

Punti buoni. Non stavo pensando al contesto. Stavo pensando al minor numero di personaggi da implementare. :)
povess

Una soluzione semplice per detta vulnerabilità: bool = nil; bool = eval(str) if ["true", "false"].include?(str)ho pensato di dover aggiungere per motivi di chiarimento.
Fernando Cordeiro
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.