_If you wish to report errors or suggest improvements for this FAQ,
please go to our
[GitHub repository](https://github.com/ruby/www.ruby-lang.org/)
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-method}

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`.

### Що таке деструктивний метод?
{: #destructive-method}

Деструктивний метод — це метод, що змінює стан об’єкта. `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
~~~
