String "true" und "false" bis boolean

85

Ich habe eine Rails-Anwendung und verwende jQuery, um meine Suchansicht im Hintergrund abzufragen. Es gibt Felder q(Suchbegriff) start_date,, end_dateund internal. Das internalFeld ist ein Kontrollkästchen und ich verwende die is(:checked)Methode, um die abgefragte URL zu erstellen:

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

Jetzt liegt mein Problem darin, params[:internal]dass es einen String gibt, der entweder "true" oder "false" enthält, und ich muss ihn in einen Booleschen Wert umwandeln. Natürlich kann ich das so machen:

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

Aber ich denke, es muss einen rubinroteren Weg geben, um mit diesem Problem umzugehen! Gibt es nicht ...?

Davidb
quelle

Antworten:

132

Soweit ich weiß, gibt es keine eingebaute Möglichkeit, Strings in Boolesche Werte umzuwandeln, aber wenn Ihre Strings nur aus bestehen 'true'und 'false'Sie Ihre Methode auf Folgendes verkürzen könnten:

def to_boolean(str)
  str == 'true'
end
cvshepherd
quelle
8
nur eine kleine Modifikation str == 'true' || str = '1'
AMTourky
30
vielleicht str.downcase == 'wahr' der Vollständigkeit
halber
@AMTourky sollte es nicht str == 'true' || sein str == '1' mit zwei "=="?
Pascal
@ Lowryder Ja! Wenn Sie das Standard-Kontrollkästchen verwenden.
7urkm3n
49

ActiveRecord bietet hierfür eine saubere Möglichkeit.

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

ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES hat alle offensichtlichen Darstellungen von wahren Werten als Zeichenfolgen.

Satya Kalluri
quelle
16
Noch einfacher, verwenden Sie einfach ActiveRecord::ConnectionAdapters::Column.value_to_boolean(string)(Quelle) apidock.com/rails/v3.0.9/ActiveRecord/ConnectionAdapters/Column/…
Mike Atlas
Ja, in den neuesten Versionen!
Satya Kalluri
6
ActiveRecord::Type::Boolean.new.type_cast_from_user("true")=> wahr ActiveRecord::Type::Boolean.new.type_cast_from_user("T")=> wahr
AlexChaffee
Falsche Werteliste wurde nach ActiveModel :: Type :: Boolean in Rails 5
verschoben
ActiveModel::Type::Booleanscheint ein viel geeigneterer Weg zu sein - obwohl er ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES"wahrheitsgemäße" Werte enthält, könnte man argumentieren, dass dies nur zufällig der Fall ist und dass Werte, die für diesen speziellen Anwendungsfall als wahrheitsgemäß angesehen werden sollten, aber nicht für andere eingeschlossen werden könnten. Auf der anderen Seite ActiveModel::Type::Booleanist offenbar so konzipiert, dass es generisch verwendet werden kann.
Lyndsy Simon
24

Sicherheitshinweis

Beachten Sie, dass diese Antwort in ihrer bloßen Form nur für den anderen unten aufgeführten Anwendungsfall und nicht für den in der Frage aufgeführten geeignet ist. Obwohl größtenteils behoben, gab es zahlreiche Sicherheitslücken im Zusammenhang mit YAML, die durch das Laden von Benutzereingaben als YAML verursacht wurden.


Ein Trick, den ich zum Konvertieren von Strings in Bools verwende, ist YAML.loadz.

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

YAML Bool akzeptiert ziemlich viele wahrheitsgemäße / falsche Zeichenfolgen:

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

Ein weiterer Anwendungsfall

Angenommen, Sie haben einen Konfigurationscode wie diesen:

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

Und in der Kommandozeile:

$ export ETC_SOMETHING=false

Da ENVvars Zeichenfolgen sind, die sich einmal im Code befinden, ist config.etc.somethingder Wert der Zeichenfolge "false"und wird fälschlicherweise ausgewertet true. Aber wenn dir das gefällt:

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

es wäre alles in Ordnung. Dies ist auch mit dem Laden von Konfigurationen aus .yml-Dateien kompatibel.

Halil Özgür
quelle
1
Das ist gut, wenn die übergebene Zeichenfolge unter Ihrer Kontrolle steht. In diesem Fall stammen die angegebenen Werte aus dem Browser des Benutzers und sollten daher als unsicher angesehen werden. Mit YAML können Sie jedes Ruby-Objekt serialisieren / deserialisieren. Dies ist möglicherweise gefährlich. Es gab zahlreiche Vorfälle: google.com/webhp?q=rails+yaml+vulnerability
Teoulas
1
@Teoulas, ich stimme dir vollkommen zu. Tatsächlich füge ich einen Hinweis hinzu, damit die Leute diesen nicht unsicher verwenden.
Halil Özgür
16

Es gibt keine eingebaute Möglichkeit, damit umzugehen (obwohl Actionpack möglicherweise einen Helfer dafür hat). Ich würde so etwas raten

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

Was auch funktioniert, ist die Verwendung von 0 und Nicht-0 anstelle von falschen / wahren Literalen:

def to_boolean(s)
  !s.to_i.zero?
end
Marcel Jackwerth
quelle
3
Sie brauchen die Wachen "s und ..." nicht, wenn Sie "!! (s = ~ / regex_here /)" verwenden, weil "nil = ~ / irgendetwas /" nil zurückgibt.
Pavling
Ah in der Tat. Ich habe es hinzugefügt, aber auch das alte beibehalten, da ich denke, dass das .matchetwas einfacher zu lesen ist.
Marcel Jackwerth
7

ActiveRecord::Type::Boolean.new.type_cast_from_usertut dies gemäß den internen Zuordnungen von Rails ConnectionAdapters::Column::TRUE_VALUESund 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

Sie können also Ihre eigene to_b(oder / to_booloder to_boolean) Methode in einem Initialisierer wie diesem erstellen:

class String
  def to_b
    ActiveRecord::Type::Boolean.new.type_cast_from_user(self)
  end
end
AlexChaffee
quelle
2
Es ist ActiveRecord :: Type :: Boolean.new.cast (Wert) in Rails 5 (siehe CWitty unten)
Dave Burt
6

Sie können wannabe_bool gem verwenden. https://github.com/prodis/wannabe_bool

Dieses Juwel implementiert eine #to_bMethode für die Klassen String, Integer, Symbol und NilClass.

params[:internal].to_b
Prodis
quelle
6

In Rails 5 können Sie ActiveRecord::Type::Boolean.new.cast(value)es in einen Booleschen Wert umwandeln.

CWitty
quelle
1
Beachten Sie jedoch, dass ActiveRecord :: Type :: Boolean.new.cast ("42") true zurückgibt
dividierenByZero
3

Ich glaube nicht, dass so etwas in Ruby eingebaut ist. Sie können die String-Klasse erneut öffnen und dort die Methode to_bool hinzufügen:

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

Dann können Sie es überall in Ihrem Projekt verwenden, wie folgt: params[:internal].to_bool

socha23
quelle
2
Ich würde definitiv keine to_boolFunktionsrückgabe haben wollen nil; das scheint falsch. Andere Konvertierungsfunktionen tun dies nicht: "a".to_ikehrt zurück 0, nichtnil
Krease
3

Vielleicht der str.to_s.downcase == 'true'Vollständigkeit halber . Dann kann nichts abstürzen, auch wenn stres null oder 0 ist.

user1839842
quelle
2

Wenn ich mir den Quellcode von Virtus anschaue , würde ich vielleicht so etwas tun:

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
quelle
1

Sie könnten betrachten nur Anfügen internalan Ihre URL , wenn es wahr ist, dann , wenn das Kontrollkästchen nicht aktiviert ist , und Sie hängen Sie es nicht params[:internal]wäre nil, das Ergebnis falsch in Ruby.

Ich bin nicht so vertraut mit der spezifischen jQuery, die Sie verwenden, aber gibt es eine sauberere Möglichkeit, das aufzurufen, was Sie möchten, als manuell eine URL-Zeichenfolge zu erstellen? Hast du dir $getund $ajaxangeschaut?

Russell
quelle
1

Sie können der String-Klasse die Methode to_boolean hinzufügen. Dann könnten Sie 'true'.to_boolean oder' 1'.to_boolean ausführen

class String
  def to_boolean
    self == 'true' || self == '1'
  end
end
Josh Cavin
quelle
-5

Ich bin überrascht, dass niemand diese einfache Lösung veröffentlicht hat. Das ist, wenn Ihre Zeichenfolgen "wahr" oder "falsch" sein werden.

def to_boolean(str)
    eval(str)
end
Armut
quelle
4
Das liegt daran, dass diese Lösung eine Sicherheitskatastrophe ist. : D
Davidb
1
Das Problem bei dieser Lösung ist die Benutzereingabe. Wenn jemand etwas eingibt to_boolean("ActiveRecord::Base.connection.execute('DROP TABLE *')"), wird Ihre Datenbank zerstört (und true zurückgegeben!). Viel Spaß: D.
Ben Aubin
Gute Argumente. Ich habe nicht an den Kontext gedacht. Ich dachte an die geringste Anzahl von Charakteren, die implementiert werden sollten. :)
Povess
Eine einfache Lösung für diese Sicherheitsanfälligkeit: bool = nil; bool = eval(str) if ["true", "false"].include?(str)Ich dachte nur, ich sollte sie zur Klarstellung hinzufügen.
Fernando Cordeiro