Apakah double-colon Ruby `::`?

427

Apa ini double-colon ::? Misalnya Foo::Bar.

Saya menemukan definisi :

Ini ::adalah operator unary yang memungkinkan: konstanta, metode instance dan metode kelas yang didefinisikan dalam kelas atau modul, dapat diakses dari mana saja di luar kelas atau modul.

Apa gunanya ruang lingkup (pribadi, terlindungi) jika Anda bisa menggunakannya ::untuk mengekspos sesuatu?

Meltemi
sumber
175
Demi manfaat para googler masa depan, jika Anda mencoba mencari simbol, coba symbolhound.com
Andrew Grimm
6
Tuhan memberkatimu, @AndrewGrimm. Itu hal terbaik yang pernah saya lihat minggu ini.
abeger

Jawaban:

381

::pada dasarnya adalah operator resolusi namespace. Ini memungkinkan Anda untuk mengakses item dalam modul, atau item tingkat kelas di kelas. Misalnya, Anda memiliki pengaturan ini:

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

Anda dapat mengakses CONSTANTdari luar modul sebagai SomeModule::InnerModule::MyClass::CONSTANT.

Itu tidak memengaruhi metode instance yang didefinisikan pada kelas, karena Anda mengaksesnya dengan sintaks yang berbeda (titik .).

Catatan yang relevan: Jika Anda ingin kembali ke namespace tingkat atas, lakukan ini: :: SomeModule - Benjamin Oakes

mipadi
sumber
5
Dalam C #, misalnya, ya. Di sisi lain C ++ (dan Ruby) digunakan ::untuk resolusi namespace sepertistd::cout << "Hello World!";
Jerry Fernholz
142
Catatan yang relevan: Jika Anda ingin kembali ke namespace tingkat atas, lakukan ini: ::SomeModule
Benjamin Oakes
5
@Benjamin Tanda titik dua tersirat, kecuali saya kebetulan memiliki SomeModule di dalam modul lain dan saya ingin mendapatkan yang tingkat atas, bukan?
Jo Liss
7
@ Jo Ya. Akan sangat membantu jika Anda ingin memastikan bahwa Anda merujuk ke konstanta di namespace tingkat atas atau konstanta dengan nama yang sama di modul lain (misalnya :: SomeOtherModule :: ClassMethods).
Benjamin Oakes
2
Ini sangat mirip dengan operan lingkup C ++
lkahtz
111

Contoh sederhana ini menggambarkannya:

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

Diambil dari http://www.tutorialspoint.com/ruby/ruby_operators.htm

Nader
sumber
inilah yang menyebabkan peringatan itu. Apakah ada cara untuk menghindari peringatan itu?
NullVoxPopuli
3
@NullVoxPopuli Secara umum memodifikasi konstanta adalah hal yang sangat buruk, tetapi jika Anda misalnya ingin memodifikasi konstanta dalam permata yang ditulis dengan buruk dan tidak ingin memotongnya , itu dapat dilakukan dengan menggunakan .send (: remove_const) ke modul yang mendefinisikan itu, lalu mendefinisikan ulang konstanta.
BookOfGreg
71

::Memungkinkan Anda mengakses konstanta, modul, atau kelas yang ditentukan di dalam kelas atau modul lain. Ini digunakan untuk menyediakan ruang nama sehingga metode dan nama kelas tidak bertentangan dengan kelas lain oleh penulis yang berbeda.

Ketika Anda melihat ActiveRecord::Basedi Rails itu berarti Rails memiliki sesuatu seperti

module ActiveRecord
  class Base
  end
end

yaitu kelas yang disebut Basedi dalam modul ActiveRecordyang kemudian dirujuk sebagai ActiveRecord::Base(Anda dapat menemukan ini di sumber Rails di activerecord-nnn / lib / active_record / base.rb)

Penggunaan umum :: adalah untuk mengakses konstanta yang didefinisikan dalam modul misalnya

module Math
  PI = 3.141 # ...
end

puts Math::PI

The ::operator tidak memungkinkan Anda untuk visibilitas memotong metode ditandai pribadi atau dilindungi.

mikej
sumber
7
Jadi jika ada class MyClass < ActiveRecord::Base, apakah itu berarti bahwa MyClass hanya mewarisi metode dari basis kelas dan bukan apa pun di dalam modul ActiveRecord?
Charlie Parker
2
Mengapa menggunakan double-colon khusus untuk resolusi namespace ini daripada menggunakan "." untuk ini juga? Konteks dan kapitalisasi akan mencegah kebingungan makna bahkan jika kita menggunakan "."
Jonah
3
@ Yunus ada beberapa kasus di mana itu akan menjadi ambigu. misalnya mempertimbangkan class Foo; Baz = 42; def self.Baz; "Baz method!"; end; end(sangat valid) Foo::Baz # => 42dan Foo.Baz # => "Baz method!". Perhatikan bahwa Foo::Baz()(dengan tanda kurung) juga akan memanggil metode.
mikej
3
Jadi use case itu memecahkannya kemampuan untuk memiliki konstanta kelas dan metode kelas yang memiliki nama yang sama persis? Itu tidak tampak seperti argumen kuat yang mendukung fitur ini. Secara pribadi saya lebih suka kehilangan kemampuan itu (sepertinya masalah, toh), kehilangan titik dua, dan menggunakan "." untuk namespacing juga .... Mungkin ada kasus penggunaan tambahan yang dipecahkan?
Jonah
26

Apa gunanya ruang lingkup (pribadi, terlindungi) jika Anda bisa menggunakan :: untuk mengekspos sesuatu?

Di Ruby, semuanya terbuka dan semuanya dapat dimodifikasi dari tempat lain.

Jika Anda khawatir tentang fakta bahwa kelas dapat diubah dari luar "definisi kelas", maka Ruby mungkin bukan untuk Anda.

Di sisi lain, jika Anda frustrasi dengan kelas Java dikunci, maka Ruby mungkin adalah yang Anda cari.

yfeldblum
sumber
1
Saya pernah mendengar beberapa rubyist mengatakan bahwa variabel instan tidak diekspos, yang bahkan attr_accessorhanya membuat metode yang mengubah variabel. (Kemudian ada lagi instance_eval)
Andrew Grimm
4
Benar, ada instance_eval. Tapi ada juga instance_variable_getdan instance_variable_set. Ruby terlalu dinamis untuk kendala.
yfeldblum
12

Menambahkan ke jawaban sebelumnya, itu sah Ruby untuk digunakan ::untuk mengakses metode contoh. Semua yang berikut ini valid:

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

Sesuai praktik terbaik saya percaya hanya yang terakhir direkomendasikan.

Yuri Ghensev
sumber
11

Tidak, ini bukan untuk mengakses setiap metode, ini adalah operator "resolusi", yaitu, Anda menggunakannya untuk menyelesaikan ruang lingkup (atau lokasi yang dapat Anda katakan) dari simbol konstan / statis.

Sebagai contoh di baris pertama Anda, Rails menggunakannya untuk menemukan kelas Base di dalam ActiveRecord.Module, yang kedua digunakan untuk menemukan metode kelas (statis) dari kelas Rute, dll, dll.

Itu tidak digunakan untuk mengekspos apa pun, itu digunakan untuk "menemukan" barang-barang di sekitar lingkup Anda.

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

Francisco Soto
sumber
dengan "(statis)" maksud Anda "(menggambar)"?!?
Meltemi
8

Anehnya, semua 10 jawaban di sini mengatakan hal yang sama. '::' adalah operator resolusi namespace, dan ya itu benar. Tetapi ada satu gotcha yang harus Anda sadari tentang operator resolusi namespace ketika datang ke algoritma pencarian konstan . Sebagai Matz menggambarkan dalam bukunya, 'Bahasa Pemrograman Ruby', pencarian konstan memiliki beberapa langkah. Pertama, ia mencari konstanta dalam lingkup leksikal di mana konstanta direferensikan. Jika tidak menemukan konstanta dalam lingkup leksikal, maka ia akan mencari hierarki warisan . Karena algoritma pencarian konstan ini, di bawah ini kami mendapatkan hasil yang diharapkan:

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

Sementara F mewarisi dari E, modul B berada dalam ruang lingkup leksikal F. Akibatnya, instance F akan merujuk ke PI konstan yang didefinisikan dalam modul B. Sekarang jika modul B tidak mendefinisikan PI, maka instance F akan merujuk ke PI konstan didefinisikan dalam superclass E.

Tetapi bagaimana jika kita menggunakan modul '::' daripada bersarang? Apakah kita akan mendapatkan hasil yang sama? Tidak!

Dengan menggunakan operator resolusi namespace ketika mendefinisikan modul bersarang, modul dan kelas bersarang tidak lagi dalam lingkup leksikal dari modul luar mereka. Seperti yang Anda lihat di bawah, PI yang didefinisikan dalam A :: B tidak dalam lingkup leksikal A :: B :: C :: D dan dengan demikian kita mendapatkan konstanta yang tidak diinisialisasi ketika mencoba merujuk ke PI dalam metode 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
sumber
4

Ini semua tentang mencegah definisi dari bentrok dengan kode lain yang terhubung ke proyek Anda. Itu berarti Anda dapat memisahkan hal-hal.

Misalnya Anda dapat memiliki satu metode yang disebut "run" di kode Anda dan Anda masih dapat memanggil metode Anda daripada metode "run" yang telah didefinisikan di beberapa perpustakaan lain yang telah Anda tautkan.

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

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

:: Digunakan untuk membuat ruang lingkup. Untuk mengakses Constant EATER dari 2 modul, kita perlu mengatur cakupan modul untuk mencapai konstanta

Francesca Rodricks
sumber
3

Ruby on rails digunakan ::untuk resolusi namespace.

class User < ActiveRecord::Base

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

end

Untuk menggunakannya:

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

Juga, penggunaan lainnya adalah: Saat menggunakan rute bersarang

OmniauthCallbacksController didefinisikan di bawah pengguna.

Dan dialihkan sebagai:

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


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end
Pankhuri
sumber