Co to jest dwukropek Ruby `::`?

427

Co to za dwukropek ::? Np Foo::Bar.

Znalazłem definicję :

::Jest operatorem, które pozwala na: stałe, przykład sposoby oraz metody klasy zdefiniowane w klasie lub modułu, aby uzyskać z dowolnego miejsca na zewnątrz klasy lub modułu.

Czym jest zakres (prywatny, chroniony), jeśli można po prostu ::coś ujawnić?

Meltemi
źródło
175
Z korzyścią dla przyszłych pracowników Google, jeśli próbujesz wyszukać symbol, spróbuj symbolhound.com
Andrew Grimm
6
Na zdrowie, @AndrewGrimm. To najlepsza rzecz, jaką widziałem w tym tygodniu.
abeger

Odpowiedzi:

381

::jest w zasadzie operatorem rozpoznawania przestrzeni nazw. Umożliwia dostęp do elementów w modułach lub przedmiotów na poziomie klasy w klasach. Załóżmy na przykład, że masz tę konfigurację:

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

Możesz uzyskać dostęp CONSTANTspoza modułu jako SomeModule::InnerModule::MyClass::CONSTANT.

Nie wpływa to na metody instancji zdefiniowane w klasie, ponieważ uzyskuje się dostęp do metod o innej składni (kropka .).

Odpowiednia uwaga: jeśli chcesz wrócić do przestrzeni nazw najwyższego poziomu, zrób to: :: SomeModule - Benjamin Oakes

mipadi
źródło
5
Na przykład w języku C # tak. Z drugiej strony użycie C ++ (i Ruby) ::do rozpoznawania przestrzeni nazw, takie jakstd::cout << "Hello World!";
Jerry Fernholz
142
Odpowiednia uwaga: jeśli chcesz wrócić do przestrzeni nazw najwyższego poziomu, zrób to: ::SomeModule
Benjamin Oakes
5
@Benjamin Wiodące dwukropki są implikowane, chyba że zdarzy się, że mam SomeModule w innym module i chcę zamiast tego dostać moduł najwyższego poziomu, prawda?
Jo Liss,
7
@Jo Tak. Może to być pomocne, jeśli chcesz się upewnić, że odwołujesz się do stałej w przestrzeni nazw najwyższego poziomu lub stałej o tej samej nazwie w innym module (np. :: SomeOtherModule :: ClassMethods).
Benjamin Oakes
2
Jest to bardzo podobne do operandu zakresu C ++
lkahtz
111

Ten prosty przykład ilustruje to:

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

Zaczerpnięte z http://www.tutorialspoint.com/ruby/ruby_operators.htm

Nader
źródło
to jednak powoduje ostrzeżenie. Czy istnieje sposób na uniknięcie ostrzeżenia?
NullVoxPopuli
3
@NullVoxPopuli Generalnie modyfikowanie stałych jest naprawdę złą rzeczą, ale jeśli na przykład chcesz zmodyfikować stałą w źle napisanym klejnocie i nie chcesz jej rozwidlać, można to zrobić za pomocą .send (: remove_const) do modułu, który definiuje to, a następnie redefiniowanie stałej.
BookOfGreg
71

::Umożliwia dostęp do stałej, modułu lub klasy zdefiniowanej w innej klasie lub module. Służy do zapewniania przestrzeni nazw, dzięki czemu nazwy metod i klas nie powodują konfliktu z innymi klasami różnych autorów.

Kiedy widzisz ActiveRecord::Basew Railsach, oznacza to, że Rails ma coś takiego

module ActiveRecord
  class Base
  end
end

tj. klasa wywoływana Basew module, do ActiveRecordktórego następnie się odwołuje ActiveRecord::Base(można to znaleźć w źródle Railsów w activerecord-nnn / lib / active_record / base.rb)

Powszechnym zastosowaniem :: jest dostęp do stałych zdefiniowanych w modułach np

module Math
  PI = 3.141 # ...
end

puts Math::PI

::Operator nie pozwalają na widoczność obejścia metod oznaczone jako prywatne lub chronione.

mikej
źródło
7
Więc jeśli tak class MyClass < ActiveRecord::Base, to czy to oznacza, że ​​MyClass dziedziczy metody tylko z klasy, a nie z niczego w module ActiveRecord?
Charlie Parker,
2
Po co używać specjalnego dwukropka dla tej rozdzielczości przestrzeni nazw zamiast używania „.” za to też? Kontekst i wielkie litery zapobiegałyby pomieszaniu znaczenia, nawet gdybyśmy używali „.”, Prawda?
Jonah
3
@Jonah jest kilka przypadków, w których byłoby to niejednoznaczne. np. rozważ class Foo; Baz = 42; def self.Baz; "Baz method!"; end; end(doskonale ważne) Foo::Baz # => 42i Foo.Baz # => "Baz method!". Zauważ, że Foo::Baz()(w nawiasach) również wywołałaby tę metodę.
mikej
3
Czyli przypadek użycia rozwiązuje zdolność do posiadania stałej klasy i metody klasy o dokładnie takiej samej nazwie? Nie wydaje się to silnym argumentem przemawiającym za tą funkcją. Osobiście wolałbym raczej stracić tę umiejętność (w każdym razie wydaje się to kłopotem), stracić dwukropek i użyć „”. dla przestrzeni nazw też .... Może istnieją dodatkowe przypadki użycia, które rozwiązuje?
Jonasz
26

Czym jest zakres (prywatny, chroniony), jeśli możesz po prostu użyć :: do ujawnienia czegokolwiek?

W Ruby wszystko jest widoczne i wszystko można modyfikować z dowolnego miejsca.

Jeśli martwisz się faktem, że klasy można zmieniać spoza „definicji klasy”, to Ruby prawdopodobnie nie jest dla ciebie.

Z drugiej strony, jeśli frustruje Cię blokowanie klas Javy, to prawdopodobnie Ruby jest tym, czego szukasz.

yfeldblum
źródło
1
Słyszałem, jak niektórzy rubiści twierdzą, że zmienne instancji nie są ujawniane, co attr_accessorczyni jedynie metody modyfikujące zmienną. (Potem znowu jest instance_eval)
Andrew Grimm,
4
Zgadza się, jest instance_eval. Ale jest też instance_variable_geti instance_variable_set. Ruby jest po prostu zbyt dynamiczny dla ograniczeń.
yfeldblum
12

Dodając do poprzednich odpowiedzi, można używać Ruby w ::celu uzyskania dostępu do metod instancji. Obowiązują wszystkie poniższe warunki:

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

Zgodnie z najlepszymi praktykami uważam, że zalecana jest tylko ostatnia.

Jurij Ghensev
źródło
11

Nie, nie ma dostępu do każdej metody, jest to operator „rozdzielczości”, to znaczy używasz go do rozwiązania zakresu (lub lokalizacji, którą możesz powiedzieć) symbolu stałego / statycznego.

Na przykład w pierwszym wierszu Railsy używają go do znajdowania klasy Base wewnątrz ActiveRecord.Module, w drugim używają go do zlokalizowania metody klasy (statycznej) klasy Routes itp.

Nie służy do niczego eksponowania, służy do „lokalizowania” rzeczy wokół zasięgu.

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

Francisco Soto
źródło
przez „(statyczny)” masz na myśli „(remis)”?!?
Meltemi
8

O dziwo, wszystkie 10 odpowiedzi tutaj mówią to samo. „::” jest operatorem rozpoznawania przestrzeni nazw i tak, to prawda. Ale jest jeden problem, który musisz wiedzieć o operatorze rozpoznawania przestrzeni nazw, jeśli chodzi o algorytm ciągłego wyszukiwania . Jak opisuje Matz w swojej książce „The Ruby Programming Language”, ciągłe wyszukiwanie ma wiele kroków. Po pierwsze, przeszukuje stałą w zakresie leksykalnym, do którego odwołuje się stała. Jeśli nie znajdzie stałej w zakresie leksykalnym, przeszukuje hierarchię dziedziczenia . Z powodu tego algorytmu ciągłego wyszukiwania poniżej otrzymujemy oczekiwane wyniki:

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

Podczas gdy F dziedziczy z E, moduł B mieści się w zakresie leksykalnym F. W związku z tym instancje F będą odnosić się do stałej PI zdefiniowanej w module B. Teraz, jeśli moduł B nie zdefiniował PI, wówczas instancje F będą odnosić się do PI stała zdefiniowana w nadklasie E.

Ale co, jeśli użyjemy „::” zamiast zagnieżdżać moduły? Czy uzyskalibyśmy ten sam wynik? Nie!

Dzięki zastosowaniu operatora rozpoznawania przestrzeni nazw podczas definiowania zagnieżdżonych modułów, zagnieżdżone moduły i klasy nie są już objęte zakresem leksykalnym ich zewnętrznych modułów. Jak widać poniżej, PI zdefiniowane w A :: B nie znajduje się w zakresie leksykalnym A :: B :: C :: D i dlatego otrzymujemy niezainicjowaną stałą podczas próby odwołania się do PI w metodzie instancji 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
Donato
źródło
4

Chodzi przede wszystkim o to, by definicje nie kolidowały z innym kodem powiązanym z twoim projektem. Oznacza to, że możesz oddzielić rzeczy od siebie.

Na przykład możesz mieć w kodzie jedną metodę o nazwie „uruchom” i nadal będziesz mógł wywoływać tę metodę zamiast metody „uruchom”, która została zdefiniowana w innej bibliotece, do której się połączyłeś.

Mongus Pong
źródło
3
module Amimal
      module Herbivorous
            EATER="plants" 
      end
end

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

:: Służy do tworzenia zakresu. Aby uzyskać dostęp do Constant EATER z 2 modułów, musimy rozszerzyć zakres modułów, aby osiągnąć stałą

Francesca Rodricks
źródło
3

Ruby on rails używa ::do rozpoznawania przestrzeni nazw.

class User < ActiveRecord::Base

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

end

Aby go użyć:

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

Inne użycie to: Podczas korzystania z zagnieżdżonych tras

OmniauthCallbacksController jest zdefiniowany w ramach użytkowników.

I kierowany jako:

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


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end
Pankhuri
źródło