Wie sende ich einen booleschen Parameter in Rails?

74

Ich sende einen Parameter show_allmit dem Wert true. Dieser Wert ist keinem Modell zugeordnet.

Mein Controller weist diesen Parameter einer Instanzvariablen zu:

@show_all = params[:show_all]

Jedoch @show_all.is_a? Stringund if @show_all == truescheitert immer.

Welche Werte analysiert Rails als Boolesche Werte? Wie kann ich explizit angeben, dass mein Parameter ein Boolescher Wert und kein String ist?

nfm
quelle
Es gibt keine Booleschen Werte in Ruby, nur TrueClassundFalseClass
NullUserException
4
Rails ermittelt es immer noch automatisch, wenn der Parameter einem Modell zugeordnet ist. Wenn der Spaltentyp in der Datenbank boolesch ist, wird der Parameter als TrueClassoder behandelt FalseClass. Irgendeine Idee, wie ich das gut machen kann?
NFM
mögliches Duplikat von Wie prüfe ich, ob ein Parameter wahr oder falsch ist? wegen "Gibt es eine bessere Möglichkeit, diese if / else-Anweisung basierend auf einem Parameter zu machen, der wahr oder falsch ist?"
Ciro Santilli 17 冠状 病 六四 事件 17
Eine gute Lösung für diese Frage könnte auch eine gute Lösung für diese Frage sein.
Ciro Santilli 法轮功 冠状 病 六四 事件 17

Antworten:

70

Ich wollte die zetetische Antwort kommentieren, aber da ich das noch nicht kann, werde ich dies als Antwort posten.

Wenn du benutzt

@show_all = params[:show_all] == "1"

Dann können Sie löschen, ? true : falseda die params[:show_all] == "1"Anweisung selbst als wahr oder falsch ausgewertet wird und daher kein ternärer Operator benötigt wird.

Uģis Ozole
quelle
Für zukünftige Referenzen ist dies die sauberste Antwort. Technisch gesehen kann der Boolesche Wert als "wahr" oder "falsch" übergeben werden, aber das Übergeben einer Zahl ist einfach besser, weil dadurch verhindert wird, dass einfache Fehler wie "wahr" == "wahr" falsch zurückgegeben werden. Entweder 1 oder 0.
Sybohy
99

UPDATE: Rails 5:

ActiveRecord::Type::Boolean.new.deserialize('0')

UPDATE: Rails 4.2 hat eine öffentliche API dafür:

ActiveRecord::Type::Boolean.new.type_cast_from_user("0") # false

VORHERIGE ANTWORT:

ActiveRecord verwaltet eine Liste mit Darstellungen für true / false unter https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/column.rb

2.0.0-p247 :005 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean("ON")
2.0.0-p247 :006 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean("F")

Dies ist nicht Teil der öffentlichen API von Rails, daher habe ich es in eine Hilfsmethode eingeschlossen:

class ApplicationController < ActionController::Base
  private

  def parse_boolean(value)
    ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
  end
end

und fügte einen Basistest hinzu:

class ApplicationControllerTest < ActionController::TestCase
  test "parses boolean params" do
    refute ApplicationController.new.send(:parse_boolean, "OFF")
    assert ApplicationController.new.send(:parse_boolean, "T")
  end
end
Wojtek Kruszewski
quelle
3
Wir sollten jedoch beachten , dass ActiveRecord::Type::Boolean.new.type_cast_from_user("0")zurückkehren wird , falsewie Sie erwähnt, aber in Rails 5 wird es beginnen wahre Rückkehr: deprecation ACHTUNG: Sie haben versucht , einen Wert zuzuweisen , die nicht explizit ist trueoder falseauf eine boolean Spalte. Derzeit wird dieser Wert auf false. Dies ändert sich entsprechend der Semantik von Ruby und wird truein Rails 5 umgewandelt. Wenn Sie das aktuelle Verhalten beibehalten möchten, sollten Sie die Werte, in die Sie umgewandelt werden möchten, explizit behandeln false.
John Smith
1
ActiveRecord::Type::Booleanist nur ein Alias ​​für ActiveModel::Type::Boolean. Es ist wahrscheinlich ein kleines bisschen besser, die ursprüngliche Klasse zu verwenden.
Tim Dorr
22

Diese Frage ist ziemlich alt, aber da ich ein paar Mal auf dieses Problem gestoßen bin und keine der vorgeschlagenen Lösungen mochte, habe ich selbst etwas gehackt, das es ermöglicht, mehrere Zeichenfolgen für wahr zu verwenden, z. B. "Ja", "Ein". , 't' und das Gegenteil für falsch.

Monkey patchen Sie die Klasse String und fügen Sie eine Methode hinzu, um sie in Boolesche Werte zu konvertieren. Fügen Sie diese Datei /config/initializerswie hier vorgeschlagen ein: Monkey Patching in Rails 3

class String
  def to_bool
    return true if ['true', '1', 'yes', 'on', 't'].include? self
    return false if ['false', '0', 'no', 'off', 'f'].include? self
    return nil
  end
end

Beachten Sie, dass wenn der Wert weder für wahr noch für falsch einer der gültigen ist, nil zurückgegeben wird. Es ist nicht dasselbe zu suchen ?paid=false(alle nicht bezahlten Datensätze zurückgeben) als ?paid=(Ich gebe nicht an, ob es bezahlt werden muss oder nicht - also verwerfen Sie dies).

Wenn Sie dann diesem Beispiel folgen, sieht die Logik in Ihrem Controller folgendermaßen aus:

Something.where(:paid => params[:paid].to_bool) unless params[:paid].try(:to_bool).nil?

Es ist ziemlich ordentlich und hilft, Controller / Modelle sauber zu halten.

ismriv
quelle
2
Eine ausgezeichnete Lösung, sauber, ordentlich und fügt der String-Klasse eine wirklich nützliche Funktionalität hinzu. Sehr schön.
Sean Cameron
19
Herzlichen Glückwunsch, Sie haben jetzt eine String#to_bool, die zurückkehren kann nil. Bitte meditieren Sie eine Weile darüber und benennen Sie die Methode dann entweder in so etwas um String#maybe_bool?oder greifen Sie darauf zurück ArgumentError.
Nicos
2
Dies könnte noch weiter vereinfacht werden, dadef to_bool; ['true', '1', 'yes', 'on', 't'].include? self; end
Lobati
5
Jedes Mal, wenn Sie diese Methode aufrufen, generieren Sie mindestens ein Array. Verschieben Sie diese Arrays in eingefrorene Konstanten. TRUE_VALUES = ['true'.freeze, '1'.freeze, 'yes'.freeze, 'on'.freeze, 't'.freeze]. Dies a) spart Speicher und b) verhindert Laufzeitänderungen der Strings.
Chris
@Nicos, obwohl ich Ihrem Kommentar zustimme, bedenken Sie, dass zumindest in Rails 3.2 (in anderen Versionen nicht getestet) ActiveRecord::ConnectionAdapters::Column.value_to_boolean('')zurückkehren wird nil.
Alexander
16
@show_all = params[:show_all] == "1" ? true : false

Dies sollte gut funktionieren, wenn Sie den Wert über ein Kontrollkästchen übergeben. Ein fehlender Schlüssel in einem Hash generiert null, was in einer Bedingung als falsch ausgewertet wird.

BEARBEITEN

Wie hier ausgeführt , ist der ternäre Operator nicht erforderlich, daher kann dies nur sein:

@show_all = params[:show_all] == "1"

zetetisch
quelle
4

Sie können Ihre Gleichstellungserklärung ändern in:

@show_all == "true"

Wenn Sie möchten, dass es sich um einen Booleschen Wert handelt, können Sie eine Methode für die Zeichenfolgenklasse erstellen, um eine Zeichenfolge in einen Booleschen Wert zu konvertieren.

benr75
quelle
3

Ich denke, die einfachste Lösung besteht darin, "boolesche" Parameter anhand ihrer String-Darstellung zu testen.

@show_all = params[:show_all]
if @show_all.to_s == "true"
   # do stuff
end

Unabhängig davon, ob Rails den Parameter als String "true" oder "false" oder als tatsächliche TrueClass oder FalseClass liefert, funktioniert dieser Test immer.

Jeremy Burton
quelle
1

Ein anderer Ansatz besteht darin, nur den Schlüssel ohne Wert zu übergeben. Obwohl die Verwendung ActiveRecord::Type::Boolean.new.type_cast_from_user(value)ziemlich ordentlich ist, kann es vorkommen, dass das Zuweisen eines Werts zum Parameterschlüssel redundant ist.

Beachten Sie Folgendes: In meiner Produktindexansicht möchte ich standardmäßig nur eine Sammlung von Produkten mit Gültigkeitsbereich anzeigen (z. B. diejenigen, die auf Lager sind). Wenn ich also alle Produkte zurückgeben möchte, kann ich myapp.com/products?show_all=trueden show_allParameter für einen booleschen Wert senden und typisieren .

Die umgekehrte Option myapp.com/products?show_all=falsemacht jedoch keinen Sinn, da dieselbe Produktkollektion zurückgegeben wird myapp.com/products, die zurückgegeben worden wäre.

Eine Alternative:

Wenn ich die gesamte nicht gespeicherte Sammlung zurückgeben möchte, sende ich myapp.com/products?allund definiere in meinem Controller

private

def show_all?
  params.key?(:all)
end

Wenn der Schlüssel in params vorhanden ist, weiß ich unabhängig von seinem Wert, dass ich alle Produkte zurückgeben muss, ohne dass ein Wert typisiert werden muss.

Chaimann
quelle
Ich habe mich entschieden, diesen Ansatz für meinen Anwendungsfall zu verwenden. Ich war in der Vergangenheit ambivalent dazu, aber wenn es keinen wirklichen Anwendungsfall für den falschen Wert gibt, ist es sinnvoll, dies so zu tun!
RSmithlal
1

Sie können Ihrem Modell Folgendes hinzufügen:

def show_all= value
  @show_all = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
end
mohamed-ibrahim
quelle
1

Du könntest es einfach tun

@show_all = params[:show_all].downcase == 'true'
Emad
quelle
0

Sie können alle Ihre booleschen Parameter wie folgt in echte Boolesche Werte konvertieren:

%w(show_all, show_featured).each do |bool_param|
  params[bool_param.to_sym] = params[bool_param.to_sym] == "true"
end

In dieser Lösung würden keine Parameter falsch werden.

Fisherwebdev
quelle
0

Es ist erwähnenswert, dass die einfachere Lösung darin besteht, einen Wert an ein ActiveModel in Rails> 5.2 zu übergeben attribute.

class Model
  include ActiveModel::Attributes

  attribute :show_all, :boolean
end

Model.new(show_all: '0').show_all # => false

Wie hier zu sehen ist .


Vor 5.2 benutze ich:

class Model
  include ActiveModel::Attributes

  attribute_reader :show_all

  def show_all=(value)
    @show_all = ActiveModel::Type::Boolean.new.cast(value)
  end
end

Model.new(show_all: '0').show_all # => false
ökoologisch
quelle