Что такое двойная двоеточие в Ruby `::`?

427

Что это за двоеточие ::? Например Foo::Bar.

Я нашел определение :

Это ::унарный оператор, который позволяет: константы, методы экземпляра и методы класса, определенные в классе или модуле, доступны из любой точки вне класса или модуля.

Что хорошего в сфере (приватной, защищенной), если вы можете просто использовать ::что-либо для разоблачения?

Мельтеми
источник
175
Для будущих гуглов, если вы пытаетесь найти символ, попробуйте symbolhound.com
Эндрю Гримм
1
Возможный дубликат? stackoverflow.com/questions/2276905/what-does-mean-in-ruby
iX3
а также stackoverflow.com/questions/3597096/…
1dolinski
6
Благослови тебя, @AndrewGrimm. Это лучшее, что я видел на этой неделе.
abeger

Ответы:

381

::в основном оператор разрешения пространства имен. Это позволяет вам получать доступ к элементам в модулях или элементам уровня класса в классах. Например, скажем, у вас была эта настройка:

module SomeModule
    module InnerModule
        class MyClass
            CONSTANT = 4
        end
    end
end

Вы можете получить доступ CONSTANTизвне модуля как SomeModule::InnerModule::MyClass::CONSTANT.

Это не влияет на методы экземпляра, определенные в классе, так как вы обращаетесь к методам с другим синтаксисом (точка .).

Соответствующее примечание: Если вы хотите вернуться к пространству имен верхнего уровня, сделайте это: :: SomeModule - Benjamin Oakes

mipadi
источник
5
В C #, например, да. С другой стороны, C ++ (и Ruby) используют ::для разрешения пространства имен, напримерstd::cout << "Hello World!";
Джерри Фернхольц,
142
Соответствующее примечание: Если вы хотите вернуться к пространству имен верхнего уровня, сделайте следующее: ::SomeModule
Бенджамин Оукс
5
@Benjamin Подразумеваются ведущие двоеточия, разве что у меня есть модуль SomeModule внутри другого модуля, и я хочу получить вместо него модуль верхнего уровня, верно?
Джо Лисс
7
@ Да, да. Это может быть полезно, если вы хотите убедиться, что вы ссылаетесь на константу в пространстве имен верхнего уровня или константу с тем же именем в другом модуле (например, :: SomeOtherModule :: ClassMethods).
Бенджамин Оукс
2
Это очень похоже на контекстный операнд C ++
lkahtz
111

Этот простой пример иллюстрирует это:

MR_COUNT = 0        # constant defined on main Object class
module Foo
  MR_COUNT = 0
  ::MR_COUNT = 1    # set global count to 1
  MR_COUNT = 2      # set local count to 2
end

puts MR_COUNT       # this is the global constant: 1
puts Foo::MR_COUNT  # this is the local constant: 2

Взято с http://www.tutorialspoint.com/ruby/ruby_operators.htm

Надер
источник
это то, что вызывает предупреждение, хотя. Есть ли способ уклониться от предупреждения?
NullVoxPopuli
3
@NullVoxPopuli Как правило, изменение констант - это очень плохо, но если вы, например, хотите изменить константу в плохо написанном геме и не хотите ее разветвлять, это можно сделать с помощью .send (: remove_const) для модуля, который определяет это, то переопределение константы.
BookOfGreg
71

::Позволяет получить доступ к константе, модулю или классу, определенному внутри другого класса или модуля. Он используется для предоставления пространств имен, чтобы имена методов и классов не конфликтовали с другими классами разных авторов.

Когда вы видите ActiveRecord::Baseв Rails, это означает, что в Rails есть что-то вроде

module ActiveRecord
  class Base
  end
end

т.е. класс, вызываемый Baseвнутри модуля, на ActiveRecordкоторый затем ссылаются как ActiveRecord::Base(вы можете найти это в источнике Rails в activerecord-nnn / lib / active_record / base.rb)

Обычно используется :: для доступа к константам, определенным в модулях, например

module Math
  PI = 3.141 # ...
end

puts Math::PI

::Оператор не позволяет обойти видимости методов отмечен частным или защищен.

MikeJ
источник
7
Так что если есть class MyClass < ActiveRecord::Base, значит ли это, что MyClass наследует только методы из базы классов, а не что-либо внутри модуля ActiveRecord?
Чарли Паркер
2
Зачем использовать специальное двойное двоеточие для разрешения пространства имен, а не "." для этого тоже? Контекст и заглавные буквы предотвратили бы путаницу смысла, даже если бы мы использовали «.», Не так ли?
Иона
3
@Джона, в некоторых случаях это было бы неоднозначно. например, рассмотреть class Foo; Baz = 42; def self.Baz; "Baz method!"; end; end(совершенно правильно) Foo::Baz # => 42и Foo.Baz # => "Baz method!". Обратите внимание, что Foo::Baz()(с круглыми скобками) также вызовет метод.
mikej
3
Таким образом, в случае использования он решает возможность иметь константу класса и метод класса с одинаковым именем? Это не похоже на сильный аргумент в пользу этой функции. Лично я бы предпочел потерять эту способность (в любом случае это кажется проблемой), потерять двойное двоеточие и использовать "." для пространства имен тоже .... Может быть, есть дополнительные варианты использования, которые он решает?
Иона
26

Что хорошего в области действия (приватной, защищенной), если вы можете просто использовать ::, чтобы выставить что-нибудь?

В Ruby все выставлено, и все можно изменить из любого места.

Если вас беспокоит тот факт, что классы могут быть изменены вне «определения класса», то, вероятно, Ruby не для вас.

С другой стороны, если вы разочарованы блокировкой классов Java, то, вероятно, вы ищете Ruby.

yfeldblum
источник
1
Я слышал, что некоторые rubyists говорят, что переменные экземпляра не выставляются, что даже attr_accessorпросто создает методы, которые изменяют переменную. (Опять же есть instance_eval)
Эндрю Гримм
4
Правильно, есть instance_eval. Но есть также instance_variable_getи instance_variable_set. Ruby слишком динамичен для ограничений.
yfeldblum
12

Добавление к предыдущим ответам позволяет использовать Ruby ::для доступа к методам экземпляра. Все следующие действительны:

MyClass::new::instance_method
MyClass::new.instance_method
MyClass.new::instance_method
MyClass.new.instance_method

В соответствии с лучшими практиками, я считаю, что рекомендуется только последний.

Юрий Генсев
источник
11

Нет, это не доступ к каждому методу, это оператор «разрешения», то есть вы используете его для разрешения области (или местоположения, которую вы можете сказать) постоянного / статического символа.

Например, в первой строке Rails использует его для поиска класса Base внутри ActiveRecord.Module, во второй - для поиска метода класса (статического) класса Routes и т. Д., И т. Д.

Он не используется для разоблачения чего-либо, он используется для «локализации» вещей вокруг ваших областей.

http://en.wikipedia.org/wiki/Scope_resolution_operator

Франсиско Сото
источник
под "(статичным)" вы подразумеваете "(рисовать)"?!?
Мелтеми
8

Удивительно, но все 10 ответов здесь говорят об одном и том же. '::' является оператором разрешения пространства имен, и да, это правда. Но есть один момент, который вы должны понять, об операторе разрешения пространства имен, когда дело доходит до алгоритма постоянного поиска . Как подчеркивает Мац в своей книге «Язык программирования Ruby», постоянный поиск состоит из нескольких шагов. Во-первых, он ищет константу в лексическом контексте, на которую ссылается константа. Если он не находит константу в лексической области, он ищет иерархию наследования . Из-за этого алгоритма постоянного поиска ниже мы получаем ожидаемые результаты:

module A
  module B
      PI = 3.14
      module C
        class E
          PI = 3.15
        end
        class F < E
          def get_pi
            puts PI
          end
        end
      end
  end
end
f = A::B::C::F.new
f.get_pi
> 3.14

Хотя F наследуется от E, модуль B находится в лексической области действия F. Следовательно, экземпляры F будут ссылаться на константу PI, определенную в модуле B. Теперь, если модуль B не определил PI, тогда экземпляры F будут ссылаться на PI. константа, определенная в суперклассе E.

Но что, если бы мы использовали «::» вместо вложенных модулей? Получим ли мы такой же результат? Нет!

При использовании оператора разрешения пространства имен при определении вложенных модулей вложенные модули и классы больше не входят в лексическую область своих внешних модулей. Как вы можете видеть ниже, PI, определенный в A :: B, не входит в лексическую область A :: B :: C :: D, и поэтому мы получаем неинициализированную константу при попытке обратиться к PI в методе экземпляра get_pi:

module A
end

module A::B
  PI = 3.14
end

module A::B::C
  class D
    def get_pi
      puts PI
    end
  end
end
d = A::B::C::D.new
d.get_pi
NameError: uninitialized constant A::B::C::D::PI
Did you mean?  A::B::PI
Донато
источник
4

Это все о предотвращении столкновения определений с другим кодом, связанным с вашим проектом. Это означает, что вы можете держать вещи отдельно.

Например, в вашем коде может быть один метод с именем «run», и вы все равно сможете вызывать его, а не метод «run», определенный в какой-то другой библиотеке, с которой вы связались.

Mongus Pong
источник
3
module Amimal
      module Herbivorous
            EATER="plants" 
      end
end

Amimal::Herbivorous::EATER => "plants"

:: Используется для создания области. Чтобы получить доступ к Constant EATER из 2 модулей, нам нужно настроить модули так, чтобы они доходили до постоянной

Франческа Родрикс
источник
3

Ruby on rails использует ::для разрешения пространства имен.

class User < ActiveRecord::Base

  VIDEOS_COUNT = 10
  Languages = { "English" => "en", "Spanish" => "es", "Mandarin Chinese" => "cn"}

end

Чтобы использовать это:

User::VIDEOS_COUNT
User::Languages
User::Languages.values_at("Spanish") => "en"

Кроме того, другое использование: при использовании вложенных маршрутов

OmniauthCallbacksController определяется под пользователями.

И маршрутизируется как:

devise_for :users, controllers: {omniauth_callbacks: "users/omniauth_callbacks"}


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end
Pankhuri
источник