¿Qué es el doble punto de Ruby `::`?

427

¿Qué es este doble punto ::? Por ej Foo::Bar.

Encontré una definición :

El ::es un operador unario que permite acceder a: constantes, métodos de instancia y métodos de clase definidos dentro de una clase o módulo, desde cualquier lugar fuera de la clase o módulo.

¿De qué sirve el alcance (privado, protegido) si solo puede usar ::para exponer algo?

Meltemi
fuente
175
Para el beneficio de los futuros googlers, si está intentando buscar un símbolo, intente symbolhound.com
Andrew Grimm
66
Dios te bendiga, @AndrewGrimm. Eso es lo mejor que he visto esta semana.
abeger

Respuestas:

381

::es básicamente un operador de resolución de espacio de nombres. Le permite acceder a elementos en módulos o elementos de nivel de clase en clases. Por ejemplo, supongamos que tenía esta configuración:

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

Puede acceder CONSTANTdesde fuera del módulo como SomeModule::InnerModule::MyClass::CONSTANT.

No afecta a los métodos de instancia definidos en una clase, ya que accede a aquellos con una sintaxis diferente (el punto .).

Nota relevante: si desea volver al espacio de nombres de nivel superior, haga esto: :: SomeModule - Benjamin Oakes

mipadi
fuente
55
En C #, por ejemplo, sí. Por otro lado, C ++ (y Ruby) se usan ::para la resolución de espacios de nombres comostd::cout << "Hello World!";
Jerry Fernholz el
142
Nota relevante: Si desea volver al espacio de nombres de nivel superior, haga esto: ::SomeModule
Benjamin Oakes
55
@Benjamin Los dos puntos principales están implícitos, a menos que tenga un SomeModule dentro de otro módulo y quiera obtener el de nivel superior, ¿correcto?
Jo Liss
77
@Jo Sí. Puede ser útil si desea asegurarse de que se refiere a una constante en el espacio de nombres de nivel superior o una constante con el mismo nombre en otro módulo (por ejemplo, :: SomeOtherModule :: ClassMethods).
Benjamin Oakes
2
Esto es muy parecido al operando de alcance de C ++
lkahtz
111

Este simple ejemplo lo ilustra:

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

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

Nader
fuente
Sin embargo, esto es lo que causa la advertencia. ¿Hay alguna forma de evadir la advertencia?
NullVoxPopuli
3
@NullVoxPopuli En general, modificar constantes es algo realmente malo, pero si, por ejemplo, desea modificar una constante en una gema mal escrita y no quiere bifurcarla, puede hacerlo usando .send (: remove_const) en el módulo que define luego, redefiniendo la constante.
BookOfGreg
71

::Le permite acceder a una constante, módulo o clase definida dentro de otra clase o módulo. Se utiliza para proporcionar espacios de nombres para que los nombres de métodos y clases no entren en conflicto con otras clases de diferentes autores.

Cuando veas ActiveRecord::Baseen Rails, significa que Rails tiene algo como

module ActiveRecord
  class Base
  end
end

es decir, una clase llamada Basedentro de un módulo al ActiveRecordque luego se hace referencia como ActiveRecord::Base(puede encontrar esto en la fuente Rails en activerecord-nnn / lib / active_record / base.rb)

Un uso común de :: es acceder a las constantes definidas en los módulos, por ejemplo

module Math
  PI = 3.141 # ...
end

puts Math::PI

El ::operador no le permite evitar la visibilidad de los métodos marcados como privados o protegidos.

mikej
fuente
77
Entonces, si uno tiene class MyClass < ActiveRecord::Base, ¿eso significa que MyClass solo hereda métodos de la base de clase y no nada dentro del módulo ActiveRecord?
Charlie Parker
2
¿Por qué usar el doble punto doble para esta resolución de espacio de nombres en lugar de usar el "." por esto también? El contexto y la capitalización evitarían la confusión de significado incluso si estuviéramos usando el ".", ¿No es así?
Jonás
3
@ Jonás hay algunos casos en los que sería ambiguo. por ejemplo, considerar class Foo; Baz = 42; def self.Baz; "Baz method!"; end; end(perfectamente válido) Foo::Baz # => 42y Foo.Baz # => "Baz method!". Tenga en cuenta que Foo::Baz()(con paréntesis) también llamaría al método.
mikej
3
Entonces, ¿el caso de uso resuelve la capacidad de tener una clase constante y un método de clase que tenga exactamente el mismo nombre? Eso no parece un argumento fuerte a favor de la función. Personalmente, preferiría perder esa habilidad (de todos modos, parece un problema), perder el doble punto y usar "." para el espacio de nombres también ... ¿Tal vez hay casos de uso adicionales que resuelve?
Jonás
26

¿De qué sirve el alcance (privado, protegido) si solo puede usar :: para exponer algo?

En Ruby, todo está expuesto y todo se puede modificar desde cualquier otro lugar.

Si le preocupa el hecho de que las clases se pueden cambiar desde fuera de la "definición de clase", entonces Ruby probablemente no sea para usted.

Por otro lado, si está frustrado por el bloqueo de las clases de Java, entonces Ruby es probablemente lo que está buscando.

Yfeldblum
fuente
1
Escuché que algunos rubíes dicen que las variables de instancia no están expuestas, que incluso attr_accessorsimplemente crea métodos que modifican la variable. (Entonces de nuevo hay instance_eval)
Andrew Grimm
44
Correcto, hay instance_eval. Pero también hay instance_variable_gety instance_variable_set. Ruby es demasiado dinámico para las restricciones.
yfeldblum
12

Además de las respuestas anteriores, es válido usar Ruby ::para acceder a los métodos de instancia. Todos los siguientes son válidos:

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

Según las mejores prácticas, creo que solo se recomienda la última.

Yuri Ghensev
fuente
11

No, no es para acceder a todos los métodos, es un operador de "resolución", es decir, lo usa para resolver el alcance (o la ubicación que puede decir) de un símbolo constante / estático.

Por ejemplo, en el primero de su línea, Rails lo usa para encontrar la clase Base dentro del ActiveRecord.Module, en su segundo se usa para localizar el método de clase (estático) de la clase Rutas, etc., etc.

No se usa para exponer nada, se usa para "localizar" cosas alrededor de sus alcances.

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

Francisco Soto
fuente
por "(estático)" quieres decir "(dibujar)"?!?
Meltemi
8

Sorprendentemente, las 10 respuestas aquí dicen lo mismo. El '::' es un operador de resolución de espacio de nombres, y sí, es cierto. Pero hay un problema que debe conocer sobre el operador de resolución de espacio de nombres cuando se trata del algoritmo de búsqueda constante . Como Matz delinea en su libro, 'El lenguaje de programación de Ruby', la búsqueda constante tiene múltiples pasos. Primero, busca una constante en el ámbito léxico donde se hace referencia a la constante. Si no encuentra la constante dentro del alcance léxico, entonces busca en la jerarquía de herencia . Debido a este algoritmo de búsqueda constante, a continuación obtenemos los 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

Mientras que F hereda de E, el módulo B está dentro del alcance léxico de F. En consecuencia, las instancias F se referirán al PI constante definido en el módulo B. Ahora, si el módulo B no definió PI, entonces las instancias F se referirán al PI constante definida en la superclase E.

Pero, ¿qué pasaría si tuviéramos que usar '::' en lugar de anidar módulos? ¿Conseguiríamos el mismo resultado? ¡No!

Al utilizar el operador de resolución de espacio de nombres al definir módulos anidados, los módulos y clases anidados ya no están dentro del alcance léxico de sus módulos externos. Como puede ver a continuación, el PI definido en A :: B no está en el ámbito léxico de A :: B :: C :: D y, por lo tanto, obtenemos una constante no inicializada al intentar hacer referencia a PI en el método de instancia 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
fuente
4

Se trata de evitar que las definiciones entren en conflicto con otro código vinculado a su proyecto. Significa que puedes mantener las cosas separadas.

Por ejemplo, puede tener un método llamado "ejecutar" en su código y aún podrá llamar a su método en lugar del método "ejecutar" que se ha definido en alguna otra biblioteca que haya vinculado.

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

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

:: Se utiliza para crear un alcance. Para acceder a Constant EATER desde 2 módulos, necesitamos establecer el alcance de los módulos para alcanzar la constante

Francesca Rodricks
fuente
3

Ruby on rails utiliza ::para la resolución de espacios de nombres.

class User < ActiveRecord::Base

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

end

Para usarlo:

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

Además, otro uso es: Al usar rutas anidadas

OmniauthCallbacksController se define bajo usuarios.

Y enrutado como:

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


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end
Pankhuri
fuente