_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 связывает все сообщения с методами динамически. Сначала он ищет синглтон-методы у получателя, затем методы, определенные в собственном классе получателя, и, наконец, методы, определенные в суперклассах получателя (включая любые модули, которые могли быть подмешаны). Вы можете увидеть порядок поиска, выведя `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-method}

Синглтон-метод (singleton method) — это метод экземпляра, связанный с одним конкретным объектом.

Вы создаете синглтон-метод, включая объект в определение:

~~~
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> (NameError)
~~~

Синглтон-методы полезны, когда вы хотите добавить метод к объекту, а создание нового подкласса нецелесообразно.

### Все эти объекты — это прекрасно, но есть ли в 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 позволяет вам легко определять аксессоры (accessors) к этим переменным экземпляра таким образом, чтобы пользователи вашего класса могли обращаться с переменными экземпляра так же, как с атрибутами. Просто используйте один или несколько из `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>'
~~~

Защищенные (protected) методы также можно вызвать только из их собственного класса или его подклассов, но их можно вызывать как в форме функции, так и с использованием получателя. Например:

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

Методы, определенные на верхнем уровне, также являются публичными по умолчанию.

### Может ли идентификатор, начинающийся с заглавной буквы, быть именем метода?

Да, может, но мы не делаем этого просто так! Если Ruby видит имя с большой буквы, за которым следует пробел, он, вероятно (в зависимости от контекста), предположит, что это константа, а не имя метода. Поэтому, если вы используете имена методов с большой буквы, всегда помните о том, что списки параметров нужно заключать в скобки, и всегда ставьте скобки сразу после имени метода без пробелов. (Этот последний совет полезен в любом случае!)

### Вызов `super` выдает `ArgumentError`.

Вызов `super` без параметров в методе передает все аргументы этого метода методу с тем же именем в суперклассе. Если количество аргументов в исходном методе не совпадает с количеством аргументов в методе более высокого уровня, возникает ошибка `ArgumentError`. Чтобы обойти это, просто вызовите `super` и передайте подходящее количество аргументов.

### Как я могу вызвать метод с тем же именем на два уровня выше?

`super` вызывает одноименный метод на один уровень выше. Если вы перегружаете метод у более далекого предка, используйте `alias`, чтобы дать ему новое имя перед тем, как скрыть его своим определением метода. Затем вы сможете вызвать его по этому новому имени.

### Как я могу вызвать оригинальный встроенный метод после его переопределения?

Внутри определения метода вы можете использовать `super`. Также вы можете использовать `alias`, чтобы дать ему альтернативное имя. Наконец, вы можете вызвать оригинальный метод как синглтон-метод `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
~~~
