Zakres stałych w modułach Ruby
Mam mały problem ze stałym zasięgiem w modułach mixin. Powiedzmy, że mam coś takiego
module Auth
USER_KEY = "user" unless defined? USER_KEY
def authorize
user_id = session[USER_KEY]
def
end
Stała USER_KEY powinna domyślnie mieć wartość "user", chyba że została już zdefiniowana. Teraz mogę mieszać to w kilku miejscach, ale w jednym z tych miejsc USER_KEY musi być inny, więc możemy mieć coś takiego
class ApplicationController < ActionController::Base
USER_KEY = "my_user"
include Auth
def test_auth
authorize
end
end
Spodziewałbym się, że USER_KEY będzie "my_user", gdy zostanie użyty w authorize, ponieważ jest już zdefiniowany, ale nadal jest" user", wzięty z definicji modułów user_key. Czy ktoś ma jakiś pomysł jak uzyskać autoryzację do korzystania z klasowej wersji USER_KEY?
5 answers
USER_KEY
zadeklarowany (nawet warunkowo) w Auth
jest globalnie znany jako Auth::USER_KEY
. Nie jest "mieszany" z włączaniem modułów, chociaż włączenie modułów może odwoływać się do klucza w sposób nie w pełni wykwalifikowany.
Jeśli chcesz, aby każdy moduł zawierający (np. ApplicationController
) mógł zdefiniować własne USER_KEY
, Spróbuj tego:
module Auth
DEFAULT_USER_KEY = 'user'
def self.included(base)
unless base.const_defined?(:USER_KEY)
base.const_set :USER_KEY, Auth::DEFAULT_USER_KEY
end
end
def authorize
user_id = session[self.class.const_get(:USER_KEY)]
end
end
class ApplicationController < ActionController::Base
USER_KEY = 'my_user'
include Auth
end
Jeśli masz zamiar zadawać sobie tyle trudu, możesz równie dobrze zrobić z tego metodę klasową:
module Auth
DEFAULT_USER_KEY = 'user'
def self.included(base)
base.extend Auth::ClassMethods
base.send :include, Auth::InstanceMethods
end
module ClassMethods
def user_key
Auth::DEFAULT_USER_KEY
end
end
module InstanceMethods
def authorize
user_id = session[self.class.user_key]
end
end
end
class ApplicationController < ActionController::Base
def self.user_key
'my_user'
end
end
Lub na poziomie klasy accessor:
module Auth
DEFAULT_USER_KEY = 'user'
def self.included(base)
base.send :attr_accessor :user_key unless base.respond_to?(:user_key=)
base.user_key ||= Auth::DEFAULT_USER_KEY
end
def authorize
user_id = session[self.class.user_key]
end
end
class ApplicationController < ActionController::Base
include Auth
self.user_key = 'my_user'
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-22 01:25:27
Stałe nie mają globalnego zasięgu w Rubim. Stałe mogą być widoczne z dowolnego zakresu, ale musisz określić, gdzie ma być znaleziona stała. Gdy rozpoczynasz nową klasę, moduł lub def, rozpoczynasz nowy zakres, a jeśli chcesz mieć stałą z innego zakresu, musisz określić, gdzie ją znaleźć.
X = 0
class C
X = 1
module M
X = 2
class D
X = 3
puts X # => 3
puts C::X # => 1
puts C::M::X # => 2
puts M::X # => 2
puts ::X # => 0
end
end
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-11 21:01:53
Oto proste rozwiązanie.
Zmiany:
- nie ma potrzeby sprawdzania istnienia
USER_KEY
. - spróbuj poszukać stałej na module/klasie odbiornika (w Twoim przypadku byłby to kontroler). Jeśli istnieje, użyj go, w przeciwnym razie użyj domyślnego modułu/klasy (zobacz poniżej, jaka jest domyślna).
.
module Auth
USER_KEY = "user"
def authorize
user_key = self.class.const_defined?(:USER_KEY) ? self.class::USER_KEY : USER_KEY
user_id = session[user_key]
def
end
Wyjaśnienie
Zachowanie, które widzisz, nie jest specyficzne dla rails, ale wynika z tego, gdzie ruby szuka stałe, jeśli nie mają jawnego zasięgu przez ::
(co nazywam" domyślną " powyżej). Stałe są sprawdzane za pomocą "zakresu leksykalnego aktualnie wykonywanego kodu". Oznacza to, że ruby najpierw szuka stałej w module (lub klasie) kodu wykonującego, a następnie przesuwa się na zewnątrz do każdego kolejnego modułu (lub klasy), aż znajdzie stałą zdefiniowaną w tym zakresie.
W kontrolerze, dzwonisz authorize
. Ale kiedy authorize
jest wykonywany, aktualnie wykonywany kod znajduje się w Auth
. Więc tam właśnie sprawdzane są stałe. Jeśli Auth nie miał USER_KEY
, ale posiada go Moduł obudowy, wtedy zostanie użyty Moduł obudowy. Przykład:
module Outer
USER_KEY = 'outer_key'
module Auth
# code here can access USER_KEY without specifying "Outer::"
# ...
end
end
Szczególnym przypadkiem tego jest środowisko wykonawcze najwyższego poziomu, które jest traktowane jako należące do klasy Object
.
USER_KEY = 'top-level-key'
module Auth
# code here can access the top-level USER_KEY (which is actually Object::USER_KEY)
# ...
end
Jedną z pułapek jest definiowanie modułu lub klasy za pomocą operatora zakresu(::
):
module Outer
USER_KEY = 'outer_key'
end
module Outer::Auth
# methods here won't be able to use USER_KEY,
# because Outer isn't lexically enclosing Auth.
# ...
end
Zauważ, że stała może być zdefiniowana znacznie później niż metoda jest zdefiniowana. Wyszukiwanie odbywa się tylko wtedy, gdy Dostęp do USER_KEY jest możliwy, więc to też działa:
module Auth
# don't define USER_KEY yet
# ...
end
# you can't call authorize here or you'll get an uninitialized constant error
Auth::USER_KEY = 'user'
# now you can call authorize.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-26 21:06:26
Jeśli twój projekt jest w Rails, lub przynajmniej wykorzystuje moduł ActiveSupport
, możesz znacznie zmniejszyć niezbędny cukier logiczny:
module Auth
extend ActiveSupport::Concern
included do
# set a global default value
unless self.const_defined?(:USER_KEY)
self.const_set :USER_KEY, 'module_user'
end
end
end
class ApplicationController < ActionController::Base
# set an application default value
USER_KEY = "default_user"
include Auth
end
class SomeController < ApplicationController
# set a value unique to a specific controller
USER_KEY = "specific_user"
end
Jestem zaskoczony, że nikt nie zasugerował takiego podejścia, widząc, jak scenariusz OP rezydował w aplikacji Rails...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-12 18:47:40
Jest o wiele prostsze rozwiązanie pytania OP niż inne odpowiedzi tutaj ujawniają:
module Foo
THIS_CONST = 'foo'
def show_const
self.class::THIS_CONST
end
end
class Bar
include Foo
THIS_CONST ='bar'
def test_it
show_const
end
end
class Baz
include Foo
def test_it
show_const
end
end
2.3.1 :004 > r = Bar.new
=> #<Bar:0x000000008be2c8>
2.3.1 :005 > r.test_it
=> "bar"
2.3.1 :006 > z = Baz.new
=> #<Baz:0x000000008658a8>
2.3.1 :007 > z.test_it
=> "foo"
To była odpowiedź @ james-a-rosen, która dała mi inspirację do wypróbowania tego. Nie chciałem iść jego trasą, ponieważ miałem kilka stałych, które są dzielone między kilka klas, każda z inną wartością, a jego metoda wyglądała jak dużo pisania.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-08 22:07:16