19 Stimmen

Warum locale Einstellung in Rails wirkt als global (bei Verwendung von Thin)?

Ich habe gerade festgestellt, dass die empfohlene Rails Weg, um locale in Ihrem Controller festlegen

before_filter :set_locale

def set_locale
  I18n.locale = params[:locale] || I18n.default_locale
end

setzt das Gebietsschema global. Der obige Code funktioniert, aber ich frage mich, ob default_locale wirklich standardmäßig, wenn man es explizit eingeben muss?

Was ich erwarten würde, ist ein Gebietsschema pro Anfrage (wie wir Sitzung pro Anfrage haben) und etwas wie zu tun:

def set_locale
  locale = params[:locale] if params[:locale]
end

Und mit I18n.default_locale sonst standardmäßig verwendet. Dies würde idealerweise mit dem optionalen Gebietsschema in path übereinstimmen:

# config/routes.rb
scope "(:locale)", :locale => /en|nl/ do
  resources :books
end

Wenn ich jetzt aus irgendeinem Grund die Gebietsschemaeinstellung in einer Aktion überspringe, wird das Gebietsschema der vorherigen Anfrage verwendet, die von einem anderen Benutzer stammen könnte!

Und besteht nicht die Gefahr von Wettlaufsituationen, da eine Anfrage die globalen I18n.locale während eine andere Anfrage (die zuvor ein anderes Gebietsschema festgelegt hat) mitten im Rendering ist?


UPDATE: Einige Details, die ich gefunden habe, aus der I18n-Dokumentation:

Setzt das aktuelle Gebietsschema pseudo-global, d.h. im Thread.current-Hash def locale=(Gebietsschema)

Jetzt möchte ich wissen, ob jede Anfrage ein eigener Thread ist.


UPDATE 2: Siehe meine Antwort zur Erklärung.

13voto

khustochka Punkte 2336

Und nun die endgültige Antwort. TL;DR Die Einstellung des Gebietsschemas wirkt nur dann global, wenn Sie Webserver mit Threads verwenden, wie Thin und Puma.

Wie ich bereits erwähnt habe, I18n.locale=

Setzt das aktuelle Gebietsschema pseudo-global, d.h. im Thread.current-Hash

Es soll also pro Anfrage funktionieren, und so funktioniert es auch in Webrick und Unicorn.

Wenn Sie jedoch einen Webserver mit Threads wie Thin oder Puma verwenden, scheint der Thread länger zu leben, und der Wert bleibt für zukünftige Anfragen erhalten, bis er explizit geändert wird. Wo ich es gelernt habe, ist von der neuen Steve Klabnik's gem anfrage_laden :

Wenn Sie einen globalen Status benötigen, haben Sie wahrscheinlich Thread.current gewählt.

<...>

Deshalb verwenden die Leute diese ausgefallenen Webserver mit Threads, wie Thin oder Puma. Aber wenn Sie Thread.current verwenden und einen dieser Server nutzen, sollten Sie aufpassen! Werte können länger bestehen bleiben, als Sie erwarten würden, und das kann zu Fehlern führen.

3voto

Haris Krajina Punkte 14008

Der empfohlene Code von oben setzt das Gebietsschema nicht global, sondern auf Anfrage.

before_filter :set_locale

def set_locale
  I18n.locale = params[:locale] || I18n.default_locale
end

Der Code wird in der Regel im BaseController platziert, so dass er vor dem Rendern jeder Seite ausgelöst und eingestellt wird. Es gibt keine Race Conditions, da jede Seite diesen Code auslöst und das I18n Gebietsschema dort berechnet wird. Sie können dies erweitern, sagen wir, sucht für Benutzer locale, als Session locale, als Anfrage-Parameter, als verwendet Englisch.

def set_locale
  I18n.locale = @user.locale || session[:locale] || params[:locale] || :en
end

Mit anderen Worten, wenn Sie die lokale Sprache auf einer Seite, sagen wir im Home-Controller, auf Deutsch einstellen und zum Dashboard-Controller wechseln, sehen Sie die Standardsprache (Englisch). Da die Änderung nicht global ist. Das ist der Grund, warum der Code im Basis-Controller platziert wird. Hoffe es macht Sinn.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X