Офіційний FAQ Ruby
If you wish to report errors or suggest improvements for this FAQ, please go to our GitHub repository and open an issue or pull request.
Методи
Як Ruby вибирає, який метод викликати?
Ruby динамічно прив’язує всі повідомлення до методів. Спершу він шукає
singleton-методи в отримувачі, потім методи, визначені у власному класі
отримувача, і нарешті методи, визначені в суперкласах отримувача
(включно з будь-якими модулями, які могли бути домішані). Порядок пошуку
можна побачити, вивівши ClassName.ancestors, який показує класи-предки
та модулі ClassName.
Якщо після пошуку серед альтернатив відповідний метод не знайдено,
Ruby намагається викликати метод method_missing, повторюючи ту саму
процедуру пошуку для нього. Це дає змогу обробляти повідомлення
до невідомих методів і часто використовується для надання динамічних
інтерфейсів класам.
module Emphasizable
def emphasize
"**#{self}**"
end
end
class String
include Emphasizable
end
String.ancestors
# => [String, Emphasizable, Comparable, Object, Kernel, BasicObject]
"Wow!".emphasize # => "**Wow!**"
Коли під час пошуку методу emphasize його не знаходять у класі String,
Ruby далі шукає в модулі Emphasizable.
Щоб перевизначити метод, який уже існує в класі отримувача,
наприклад String#capitalize, потрібно вставити модуль у ланцюжок
предків перед цим класом, використавши prepend:
module PrettyCapitalize
def capitalize
"**#{super}**"
end
end
class String
prepend PrettyCapitalize
end
String.ancestors
# => [PrettyCapitalize, String, Comparable, Object, Kernel, BasicObject]
"hello".capitalize # => "**Hello**"
Чи є +, -, *, … операторами?
+, - та подібні — це не оператори, а виклики методів.
Тому їх можна перевантажувати новими визначеннями.
class MyString < String
def -(other)
self[0...other.size] # self truncated to other's size
end
end
Однак наведені нижче — вбудовані керувальні структури, а не методи, тому їх не можна перевизначити:
=, .., ..., not, ||, &&, and, or, ::
Щоб перевантажити або визначити унарні оператори + і -,
потрібно використовувати +@ і -@ як назви методів.
Оператор = використовується для визначення методу, що встановлює
атрибут об’єкта:
class Test
def attribute=(val)
@attribute = val
end
end
t = Test.new
t.attribute = 1
Якщо визначені оператори на кшталт + і -, Ruby автоматично обробляє
форми самоприсвоєння (+=, -= тощо).
Де ++ і --?
У Ruby немає операторів автоінкременту та автодекременту.
Замість них можна використати += 1 і -= 1.
Що таке singleton-метод?
Singleton-метод — це метод екземпляра, пов’язаний з одним конкретним об’єктом.
Singleton-метод створюють, включаючи об’єкт у визначення:
class Foo; end
foo = Foo.new
bar = Foo.new
def foo.hello
puts "Hello"
end
foo.hello
bar.hello
Виведе:
Hello
prog.rb:11:in `<main>': undefined method `hello' for #<Foo:0x000000010f5a40> (NoMethodError)
Singleton-методи корисні, коли потрібно додати метод до об’єкта, а створення нового підкласу недоцільне.
Усі ці об’єкти — добре, але чи має Ruby прості функції?
І так, і ні. У Ruby є методи, які виглядають як функції в мовах на кшталт C або Perl:
def hello(name)
puts "Hello, #{name}!"
end
hello("World")
Виведе:
Hello, World!
Однак насправді це виклики методів без явного отримувача.
У цьому випадку Ruby вважає, що отримувач — це self.
Отже, hello схожа на функцію, але насправді це метод, що належить класу
Object, і надсилається як повідомлення прихованому отримувачу self.
Ruby — чисто об’єктно-орієнтована мова.
Звісно, ви можете використовувати такі методи так, ніби це функції.
Звідки беруться всі ці методи, схожі на функції?
Майже всі класи в Ruby походять від класу Object. Визначення класу
Object домішує методи, визначені в модулі Kernel.
Тому ці методи доступні в кожному об’єкті системи.
Навіть якщо ви пишете просту програму Ruby без класів, ви фактично
працюєте всередині класу Object.
Чи можу я отримати доступ до змінних екземпляра об’єкта?
Змінні екземпляра об’єкта (ті, що починаються з @) безпосередньо
недоступні поза об’єктом. Це забезпечує хорошу інкапсуляцію.
Проте Ruby спрощує визначення методів доступу до цих змінних екземпляра
так, щоб користувачі вашого класу могли поводитися з ними як з атрибутами.
Просто використайте один або кілька з attr_reader, attr_writer
чи attr_accessor.
class Person
attr_reader :name # read only
attr_accessor :wearing_a_hat # read/write
def initialize(name)
@name = name
end
end
p = Person.new("Dave")
p.name # => "Dave"
p.wearing_a_hat # => nil
p.wearing_a_hat = true
p.wearing_a_hat # => true
Ви також можете визначити власні методи доступу (наприклад, для валідації
або обробки похідних атрибутів). Метод читання — це просто метод без
параметрів, а метод присвоєння — це метод із назвою, що закінчується на
=, і який приймає один параметр. Хоча у визначенні методу між назвою
методу та = не може бути пробілу, під час виклику можна вставляти
пробіли, і це виглядатиме як звичайне присвоєння. Також можна
використовувати самоприсвоєння на кшталт += і -=, якщо визначені
відповідні методи + або -.
У чому різниця між private і protected?
Ключове слово видимості private робить метод викличним лише у
функціональній формі, без явного отримувача, і тому його отримувачем може
бути лише self. Приватний метод можна викликати лише в межах класу,
в якому він визначений, або в його підкласах.
class Test
def foo
99
end
def test(other)
p foo
p other.foo
end
end
t1 = Test.new
t2 = Test.new
t1.test(t2)
# Now make `foo' private
class Test
private :foo
end
t1.test(t2)
Виведе:
99
99
99
prog.rb:8:in `test': private method `foo' called for #<Test:0x00000000b57a48> (NoMethodError)
from prog.rb:23:in `<main>'
Захищені методи також можна викликати лише всередині свого класу або його підкласів, але їх можна викликати як у функціональній формі, так і з отримувачем. Наприклад:
def <=>(other)
age <=> other.age
end
Код скомпілюється, якщо age — захищений метод, але не якщо він приватний.
Ці можливості допомагають контролювати доступ до внутрішньої реалізації вашого класу.
Як змінити видимість методу?
Видимість методів змінюють за допомогою private, protected і public.
Якщо використовувати їх без параметрів під час визначення класу, вони
впливають на видимість наступних методів. Якщо використовувати з
параметрами, вони змінюють видимість названих методів.
class Foo
def test
puts "hello"
end
private :test
end
foo = Foo.new
foo.test
Виведе:
prog.rb:9:in `<main>': private method `test' called for #<Foo:0x0000000284dda0> (NoMethodError)
Зробити метод класу приватним можна за допомогою private_class_method.
class Foo
def self.test
puts "hello"
end
private_class_method :test
end
Foo.test
Виведе:
prog.rb:8:in `<main>': private method `test' called for Foo:Class (NoMethodError)
Видимість за замовчуванням для методів, визначених у класі, — public.
Виняток — метод ініціалізації екземпляра initialize.
Методи, визначені на верхньому рівні, також є public за замовчуванням.
Чи може ідентифікатор, що починається з великої літери, бути назвою методу?
Так, може, але робити так не варто без потреби! Якщо Ruby бачить назву з великої літери, за якою йде пробіл, він, ймовірно (залежно від контексту), вважатиме її константою, а не назвою методу. Тож якщо ви використовуєте імена методів з великої літери, завжди пам’ятайте ставити список параметрів у дужках і розміщувати дужки відразу після імені методу без пробілів. (Остання порада корисна в будь-якому разі!)
Виклик super спричиняє ArgumentError.
Виклик super без параметрів у методі передає всі аргументи цього методу
методу з таким самим ім’ям у суперкласі. Якщо кількість аргументів у
початкового методу не збігається з кількістю у методу вищого рівня,
виникає ArgumentError. Щоб обійти це, просто викличте super і
передайте потрібну кількість аргументів.
Як викликати метод з таким самим ім’ям на два рівні вище?
super викликає однойменний метод на один рівень вище. Якщо ви
перевантажуєте метод у більш віддаленому предку, використайте alias,
щоб надати йому нове ім’я до того, як замаскуєте його своїм визначенням
методу. Потім можна викликати його за цим псевдонімом.
Як викликати оригінальний вбудований метод після його перевизначення?
Усередині визначення методу можна використати super. Також можна
застосувати alias, щоб надати йому іншу назву. Нарешті, можна викликати
оригінальний метод як singleton-метод Kernel.
Що таке деструктивний метод?
Деструктивний метод — це метод, що змінює стан об’єкта. String,
Array, Hash та інші мають такі методи. Часто є дві версії методу:
одна зі звичайною назвою, інша — з тією ж назвою, але з ! наприкінці.
Звичайна версія створює копію отримувача, вносить до неї зміни й
повертає копію. Версія з ! модифікує отримувача на місці.
Втім, слід пам’ятати, що є чимало деструктивних методів без !, зокрема
методи присвоєння (name=), присвоєння елементів масиву ([]=) і методи
на кшталт Array.delete.
Чому деструктивні методи можуть бути небезпечними?
Пам’ятайте, що присвоєння в більшості випадків лише копіює посилання на об’єкт, а передавання параметрів еквівалентне присвоєнню. Це означає, що ви можете отримати кілька змінних, які посилаються на той самий об’єкт. Якщо одна з них викликає деструктивний метод, зміниться об’єкт, на який посилаються всі.
def foo(str)
str.sub!(/foo/, "baz")
end
obj = "foo"
foo(obj) # => "baz"
obj # => "baz"
У цьому випадку змінюється фактичний аргумент.
Чи можу я повертати з методу кілька значень?
Так і ні.
def m1
return 1, 2, 3
end
def m2
[1, 2, 3]
end
m1 # => [1, 2, 3]
m2 # => [1, 2, 3]
Отже, повертається лише одна річ, але нею може бути довільно складний об’єкт. У випадку масивів можна використати множинне присвоєння, щоб отримати ефект кількох повернених значень. Наприклад:
def foo
[20, 4, 17]
end
a, b, c = foo
a # => 20
b # => 4
c # => 17