Qu'est-ce que le double-colon de Ruby `::`?

427

Qu'est-ce que ce double colon ::? Par exemple Foo::Bar.

j'ai trouvé un définition :

le :: est un opérateur unaire qui permet: des constantes, des méthodes d'instances et des méthodes de classes définies au sein d' une classe ou d'un module, pour être accessible à partir de n'importe où en dehors de la classe ou du module.

À quoi sert la portée (privée, protégée) si vous pouvez simplement l'utiliser ::pour exposer quelque chose?

Meltemi
la source
175
Pour le bénéfice des futurs googleurs, si vous essayez de rechercher un symbole, essayez symbolhound.com
Andrew Grimm
6
Que Dieu vous bénisse, @AndrewGrimm. C'est la meilleure chose que j'ai vue cette semaine.
abeger

Réponses:

381

::est fondamentalement un opérateur de résolution d'espace de noms. Il vous permet d'accéder aux éléments des modules ou aux éléments de niveau classe des classes. Par exemple, supposons que vous disposiez de cette configuration:

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

Vous pouvez accéder CONSTANTde l'extérieur du module en tant que SomeModule::InnerModule::MyClass::CONSTANT.

Cela n'affecte pas les méthodes d'instance définies sur une classe, car vous accédez à celles dont la syntaxe est différente (le point .).

Remarque pertinente: Si vous souhaitez revenir à l'espace de noms de niveau supérieur, procédez comme suit: :: SomeModule - Benjamin Oakes

mipadi
la source
5
En C #, par exemple, oui. D'autre part, l'utilisation de C ++ (et Ruby) ::pour la résolution d'espaces de noms tels questd::cout << "Hello World!";
Jerry Fernholz
142
Remarque pertinente: Si vous souhaitez revenir à l'espace de noms de niveau supérieur, procédez comme suit: ::SomeModule
Benjamin Oakes
5
@Benjamin Les deux points principaux sont implicites, à moins que je ne trouve un SomeModule dans un autre module et que je veuille obtenir celui de niveau supérieur à la place, n'est-ce pas?
Jo Liss
7
@Jo Oui. Cela peut être utile si vous voulez vous assurer que vous faites référence à une constante au niveau de l'espace de noms de niveau supérieur ou à une constante portant le même nom dans un autre module (par exemple :: SomeOtherModule :: ClassMethods).
Benjamin Oakes
2
Cela ressemble beaucoup à l'opérande scope de C ++
lkahtz
111

Cet exemple simple l'illustre:

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

Tiré de http://www.tutorialspoint.com/ruby/ruby_operators.htm

Nader
la source
c'est pourtant ce qui provoque l'avertissement. Existe-t-il un moyen d'échapper à l'avertissement?
NullVoxPopuli
3
@NullVoxPopuli Généralement, la modification des constantes est une très mauvaise chose, mais si vous voulez par exemple modifier une constante dans une gemme mal écrite et que vous ne voulez pas la bifurquer, cela pourrait être fait en utilisant .send (: remove_const) au module qui définit puis redéfinir la constante.
BookOfGreg
71

::Vous permet d'accéder à une constante, un module ou une classe définie dans une autre classe ou module. Il est utilisé pour fournir des espaces de noms afin que les noms de méthode et de classe n'entrent pas en conflit avec d'autres classes de différents auteurs.

Lorsque vous voyez ActiveRecord::Basedans Rails, cela signifie que Rails a quelque chose comme

module ActiveRecord
  class Base
  end
end

c'est-à-dire une classe appelée à l' Baseintérieur d'un module ActiveRecordqui est ensuite référencé comme ActiveRecord::Base(vous pouvez le trouver dans la source Rails dans activerecord-nnn / lib / active_record / base.rb)

Une utilisation courante de :: est d'accéder aux constantes définies dans les modules, par exemple

module Math
  PI = 3.141 # ...
end

puts Math::PI

L' ::opérateur ne vous permet pas de contourner la visibilité des méthodes marquées privées ou protégées.

mikej
la source
7
Donc, si c'est le cas class MyClass < ActiveRecord::Base, cela signifie-t-il que MyClass hérite uniquement des méthodes de la base de classe et pas de quoi que ce soit à l'intérieur du module ActiveRecord?
Charlie Parker
2
Pourquoi utiliser le double-point spécial pour cette résolution d'espace de noms plutôt que d'utiliser le "." pour cela aussi? Le contexte et la capitalisation empêcheraient la confusion de sens même si nous utilisions le ".", N'est-ce pas?
Jonah
3
@Jonah il y a des cas où ce serait ambigu. par exemple considérer class Foo; Baz = 42; def self.Baz; "Baz method!"; end; end(parfaitement valable) Foo::Baz # => 42et Foo.Baz # => "Baz method!". Notez que Foo::Baz()(avec des parenthèses) appelle également la méthode.
mikej
3
Donc, le cas d'utilisation, il résout la possibilité d'avoir une constante de classe et une méthode de classe qui ont exactement le même nom? Cela ne semble pas être un argument solide en faveur de la fonctionnalité. Personnellement, je préférerais de loin perdre cette capacité (cela semble être un problème, de toute façon), perdre les deux points et utiliser "." pour l'espace de noms aussi ... Peut-être qu'il y a des cas d'utilisation supplémentaires qu'il résout?
Jonah
26

À quoi sert la portée (privée, protégée) si vous pouvez simplement utiliser :: pour exposer quoi que ce soit?

Dans Ruby, tout est exposé et tout peut être modifié de n'importe où ailleurs.

Si vous vous inquiétez du fait que les classes peuvent être modifiées depuis l'extérieur de la "définition de classe", alors Ruby n'est probablement pas pour vous.

D'un autre côté, si vous êtes frustré par le verrouillage des classes Java, alors Ruby est probablement ce que vous recherchez.

yfeldblum
la source
1
J'ai entendu certains rubyistes dire que les variables d'instance ne sont pas exposées, ce qui fait même attr_accessorsimplement des méthodes qui modifient la variable. (Là encore, il y a instance_eval)
Andrew Grimm
4
Correct, il y a instance_eval . Mais il y a aussi instance_variable_getet instance_variable_set. Ruby est tout simplement trop dynamique pour les contraintes.
yfeldblum
12

En plus des réponses précédentes, il est valide d'utiliser Ruby ::pour accéder aux méthodes d'instance. Tous les éléments suivants sont valables:

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

Selon les meilleures pratiques, je crois que seule la dernière est recommandée.

Yuri Ghensev
la source
11

Non, ce n'est pas pour accéder à toutes les méthodes, c'est un opérateur de «résolution», c'est-à-dire que vous l'utilisez pour résoudre la portée (ou l'emplacement que vous pouvez dire) d'un symbole constant / statique.

Par exemple, dans le premier de votre ligne, Rails l'utilise pour trouver la classe de base dans ActiveRecord.Module, dans votre deuxième, il est utilisé pour localiser la méthode de classe (statique) de la classe Routes, etc., etc.

Il n'est pas utilisé pour exposer quoi que ce soit, il sert à «localiser» des éléments autour de vos oscilloscopes.

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

Francisco Soto
la source
par "(statique)" voulez-vous dire "(dessiner)"?!?
Meltemi
8

Étonnamment, les 10 réponses ici disent la même chose. Le '::' est un opérateur de résolution d'espace de noms, et oui c'est vrai. Mais il y a un problème que vous devez réaliser à propos de l'opérateur de résolution d'espace de noms en ce qui concerne l' algorithme de recherche constante . Comme Matz le décrit dans son livre, «Le langage de programmation Ruby», la recherche constante comporte plusieurs étapes. Premièrement, il recherche une constante dans la portée lexicale où la constante est référencée. S'il ne trouve pas la constante dans la portée lexicale, il recherche alors la hiérarchie d'héritage . En raison de cet algorithme de recherche constante, nous obtenons ci-dessous les résultats attendus:

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

Alors que F hérite de E, le module B est dans la portée lexicale de F. Par conséquent, les instances F se référeront à la constante PI définie dans le module B. Maintenant, si le module B n'a pas défini PI, alors les instances F se référeront au PI constante définie dans la superclasse E.

Mais que se passerait-il si nous utilisions '::' plutôt que d'imbriquer des modules? Aurions-nous le même résultat? Non!

En utilisant l'opérateur de résolution d'espace de noms lors de la définition des modules imbriqués, les modules et classes imbriqués ne sont plus dans la portée lexicale de leurs modules externes. Comme vous pouvez le voir ci-dessous, PI défini dans A :: B n'est pas dans la portée lexicale de A :: B :: C :: D et donc nous obtenons une constante non initialisée lorsque nous essayons de faire référence à PI dans la méthode d'instance 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
la source
4

Il s'agit d'empêcher les définitions de se heurter à d'autres codes liés à votre projet. Cela signifie que vous pouvez séparer les choses.

Par exemple, vous pouvez avoir une méthode appelée "exécuter" dans votre code et vous pourrez toujours appeler votre méthode plutôt que la méthode "exécuter" qui a été définie dans une autre bibliothèque que vous avez liée.

Mongus Pong
la source
3
module Amimal
      module Herbivorous
            EATER="plants" 
      end
end

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

:: Est utilisé pour créer une portée. Afin d'accéder à Constant EATER à partir de 2 modules, nous devons cibler les modules pour atteindre la constante

Francesca Rodricks
la source
3

Ruby on rails utilise ::pour la résolution d'espace de noms.

class User < ActiveRecord::Base

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

end

Pour l'utiliser :

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

En outre, d'autres utilisations sont: lors de l'utilisation d'itinéraires imbriqués

OmniauthCallbacksController est défini sous utilisateurs.

Et acheminé comme:

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


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end
Pankhuri
la source