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

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

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.


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.


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

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

2
Es ist ActiveRecord :: Type :: Boolean.new.cast (Wert) in Rails 5 (siehe CWitty unten)
Dave Burt


6

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


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


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.


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

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?


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

-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

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.