O que é o cólon duplo do Ruby `::`?

427

O que é esse cólon duplo ::? Por exemplo Foo::Bar.

Eu encontrei uma definição :

O ::é um operador unário que permite: constantes, métodos de instância e métodos de classe definidos em uma classe ou módulo, sejam acessados ​​de qualquer lugar fora da classe ou módulo.

Qual a utilidade do escopo (privado, protegido) se você puder apenas usar ::para expor alguma coisa?

Meltemi
fonte
175
Para o benefício de futuros Googlers, se você está tentando procurar um símbolo, tente symbolhound.com
Andrew Grimm
6
Que benção, @AndrewGrimm. Essa é a melhor coisa que vi esta semana.
abeger

Respostas:

381

::é basicamente um operador de resolução de espaço para nome. Permite acessar itens em módulos ou itens em nível de classe nas classes. Por exemplo, digamos que você tenha esta configuração:

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

Você pode acessar CONSTANTde fora do módulo como SomeModule::InnerModule::MyClass::CONSTANT.

Isso não afeta os métodos de instância definidos em uma classe, pois você acessa aqueles com uma sintaxe diferente (o ponto .).

Nota relevante: Se você deseja retornar ao namespace de nível superior, faça o seguinte: :: SomeModule - Benjamin Oakes

mipadi
fonte
5
Em c #, por exemplo, sim. Por outro lado, C ++ (e Ruby) usam ::para resolução de namespace, comostd::cout << "Hello World!";
Jerry Fernholz
142
Nota relevante: Se você deseja voltar ao espaço para nome de nível superior, faça o seguinte: ::SomeModule
Benjamin Oakes
5
@ Benjamin Os dois pontos principais estão implícitos, a menos que eu tenha um SomeModule dentro de outro módulo e que eu queira obter o de nível superior, correto?
Jo Liss
7
@Jo Sim. Pode ser útil se você quiser ter certeza de que está se referindo a uma constante no namespace de nível superior ou a uma constante com o mesmo nome em outro módulo (por exemplo: :: SomeOtherModule :: ClassMethods).
Benjamin Oakes
2
Isto é muito semelhante operando âmbito de C ++
lkahtz
111

Este exemplo simples ilustra isso:

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

Retirado de http://www.tutorialspoint.com/ruby/ruby_operators.htm

Nader
fonte
é isso que causa o aviso. Existe uma maneira de evitar o aviso?
NullVoxPopuli
3
@NullVoxPopuli Geralmente, modificar constantes é uma coisa muito ruim, mas se você, por exemplo, deseja modificar uma constante em uma gema mal escrita e não deseja bifurcá-la, isso pode ser feito usando .send (: remove_const) no módulo que define redefinindo a constante.
BookOfGreg 15/02
71

::Permite acessar uma constante, módulo ou classe definida dentro de outra classe ou módulo. É usado para fornecer espaços de nomes para que os nomes de métodos e classes não entrem em conflito com outras classes por autores diferentes.

Quando você vê ActiveRecord::Baseno Rails, significa que o Rails tem algo como

module ActiveRecord
  class Base
  end
end

ou seja, uma classe chamada Basedentro de um módulo ActiveRecordque é referenciada como ActiveRecord::Base(você pode encontrar isso na fonte Rails em activerecord-nnn / lib / active_record / base.rb)

Um uso comum de :: é acessar constantes definidas em módulos, por exemplo

module Math
  PI = 3.141 # ...
end

puts Math::PI

O ::operador não permite ignorar a visibilidade dos métodos marcados como privados ou protegidos.

mikej
fonte
7
Então, se houver class MyClass < ActiveRecord::Base, isso significa que o MyClass herda apenas métodos da base da classe e não nada dentro do módulo ActiveRecord?
Charlie Parker
2
Por que usar dois-pontos especiais para esta resolução de namespace, em vez de usar o "." por isso também? Contexto e letras maiúsculas impediriam confusão de significado, mesmo se estivéssemos usando o ".", Não?
Jonah
3
@ Jonah, existem alguns casos em que isso seria ambíguo. por exemplo, considere class Foo; Baz = 42; def self.Baz; "Baz method!"; end; end(perfeitamente válido) Foo::Baz # => 42e Foo.Baz # => "Baz method!". Observe que Foo::Baz()(com parênteses) também chamaria o método.
Mikej 6/04
3
Portanto, o caso de uso resolve a capacidade de ter constante de classe e um método de classe que tem exatamente o mesmo nome? Isso não parece ser um argumento forte a favor do recurso. Pessoalmente, prefiro perder essa capacidade (parece um problema, enfim), perder dois pontos e usar "." para namespacing também .... Talvez haja casos de uso adicionais que ele resolva?
Jonah
26

De que serve o escopo (privado, protegido) se você pode simplesmente usar :: para expor alguma coisa?

No Ruby, tudo é exposto e tudo pode ser modificado de qualquer outro lugar.

Se você está preocupado com o fato de que as classes podem ser alteradas de fora da "definição de classe", Ruby provavelmente não é para você.

Por outro lado, se você está frustrado com o bloqueio das classes de Java, Ruby é provavelmente o que você está procurando.

yfeldblum
fonte
1
Eu ouvi alguns rubiistas dizerem que variáveis ​​de instância não são expostas, que attr_accessorapenas cria métodos que modificam a variável. (Então, novamente há instance_eval)
Andrew Grimm
4
Correto, há instance_eval. Mas há também instance_variable_gete instance_variable_set. Ruby é dinâmico demais para restrições.
yfeldblum
12

Adicionando respostas anteriores, é válido usar Ruby ::para acessar métodos de instância. Todos os seguintes são válidos:

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

De acordo com as práticas recomendadas, acredito que apenas a última seja recomendada.

Yuri Ghensev
fonte
11

Não, não é para acessar todos os métodos, é um operador de "resolução", ou seja, você o utiliza para resolver o escopo (ou o local que você pode dizer) de um símbolo constante / estático.

Por exemplo, no primeiro da sua linha, o Rails o utiliza para encontrar a classe Base dentro do ActiveRecord.Module, no segundo é usado para localizar o método da classe (estático) da classe Routes, etc.

Não é usado para expor nada, é usado para "localizar" coisas em torno de seus escopos.

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

Francisco Soto
fonte
por "(estático)", você quer dizer "(desenhar)"?!?
Meltemi
8

Surpreendentemente, todas as 10 respostas aqui dizem a mesma coisa. O '::' é um operador de resolução de espaço para nome e sim, é verdade. Mas há um problema que você precisa entender sobre o operador de resolução do espaço para nome quando se trata do algoritmo de pesquisa constante . Como Matz delineia em seu livro 'The Ruby Programming Language', a pesquisa constante tem várias etapas. Primeiro, ele pesquisa uma constante no escopo lexical onde a constante é referenciada. Se não encontrar a constante no escopo lexical, ela procurará na hierarquia de herança . Devido a esse algoritmo de pesquisa constante, abaixo obtemos os resultados esperados:

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

Enquanto F herda de E, o módulo B está dentro do escopo lexical de F. Consequentemente, as instâncias F se referem à PI constante definida no módulo B. Agora, se o módulo B não definiu PI, as instâncias F se referem ao PI constante definida na superclasse E.

Mas e se usássemos '::' em vez de aninhar módulos? Teríamos o mesmo resultado? Não!

Ao usar o operador de resolução de espaço para nome ao definir módulos aninhados, os módulos e as classes aninhadas não estão mais dentro do escopo lexical de seus módulos externos. Como você pode ver abaixo, o PI definido em A :: B não está no escopo lexical de A :: B :: C :: D e, portanto, obtemos constante não inicializada ao tentar fazer referência ao PI no método de instância 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
fonte
4

Trata-se de impedir que as definições colidam com outro código vinculado ao seu projeto. Isso significa que você pode manter as coisas separadas.

Por exemplo, você pode ter um método chamado "run" em seu código e ainda poderá chamar seu método em vez do método "run" que foi definido em alguma outra biblioteca na qual você vinculou.

Mongus Pong
fonte
3
module Amimal
      module Herbivorous
            EATER="plants" 
      end
end

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

:: É usado para criar um escopo. Para acessar o Constant EATER a partir de 2 módulos, precisamos definir o escopo dos módulos para alcançar a constante

Francesca Rodricks
fonte
3

Ruby on rails usa ::para resolução de namespace.

class User < ActiveRecord::Base

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

end

Para usá-lo :

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

Além disso, outro uso é: Ao usar rotas aninhadas

OmniauthCallbacksController é definido em usuários.

E roteado como:

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


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end
Pankhuri
fonte