Методы


Метод — это определенное действие объекта.

Методы свойственны только объектно-ориентированному программированию. Метод это один из многих способов структурирования кода, служит чтобы действие объекта вынести отдельно и использовать несколько раз.

Методы

Объявление метода происходит при помощи выражения def после которого следует имя метода и перечень аргументов необходимых для него, пример:

def sum(a, b)
puts "#{a} + #{b} = #{a+b}"
end

sum(1, 9) #=> puts "#{a} + #{b} = #{a+b}"



Для методов не нужно явно указывать возвращаемое значение, они вернут результат выполнения посленей строки своего кода, однако иногда полезно явно указывать возвращаемое значение, при помощи выражения return.
Это явное указание методу, что нужно вернуть. Еще return следует использовать тогда, когда вас не устаивает то, что метод возвращает результат выполнения последней строки. Пример использования return:

def sum(a, b)
puts "#{a} + #{b} = #{a+b}"
return a + b
end

sum(10, 5) #=> 15



Иногда нужно вернуть больше одного значения, поэтому возвращают массив. Чтобы всегда возвращался массив используют оператор splat «*»
По такому шаблону [*array]. Пример
return [*hrefs]


Аргументы методов

Аргументы методов — это параметры, которые передаются в метод и с которыми метод выполняет определенные действия или из-за которых метод изменяет свое поведение. Аргументы бывают предопределенными (необязательными), действие по умолчанию, если аргумент не получен и не предопределенными. Предопределенные аргументы используются тогда, когда, например значение аргумента в большинстве случаев одинаково, однако, пользователю метода может понадобится его изменить.

Не предопределенные аргументы были приведены в примерах выше. Когда вы используете не предопределенные аргументы метода, например в количестве 2, но при вызове метода передаете только один аргумент, то метод вернет ошибку: ArgumentError: wrong number of arguments (1 for 2), если же один из двух аргументов будет предопределенным, то значение автоматически присваивается аргументу, который является не предопределенным. Пример предопределенных аргументов:

def sum(a=10, b=25, c=15)
puts "a = #{a}\nb = #{b}\nc = #{c}"
puts a + b + c
end

sum(15)
#a = 15
#b = 25
#c = 15
#55


Обратите внимание мы указали только первый аргумент, то есть a = 15, остальные аргументы получили свои значения из аргументов по умолчанию, указанные при объявлении метода!
Если вы будете использовать другой порядок следования аргументов, то присвоение аргументам значения будет происходить несколько неожиданно:

def sum(a=5,b=6,c)
puts a,b,c
end

sum 2
#5
#6
#2

sum 2,3
#2
#6
#3


Методы в Ruby могут принимать не только предопределенное количество аргументов, но и произвольное их количество. Если быть совсем точным, то один из аргументов метода мы объявляем коллекцией значений(массивов или хешом) при помощи оператора splat «*». В методе может быть всего один такой аргумент. Пример:

def do_collection(m,*strings)
result = []
strings.each{|str| result << str.send(m)}
return result
end

collection = %w{hello readers}
#=> ["hello", "readers"]

p result_collection = do_collection(:upcase,*collection)
#=> ["HELLO", "READERS"]

p result_collection = do_collection(:downcase,*result_collection)
#=> ["hello", "readers"]



При передаче аргументов в метод в виде массива мы также должны использовать оператор *, который превращает массив в набор значений, иначе аргументу strings будет определено одно значение — массив, что нас не устраивает.

Еще пример, где можно передавать не определенные аргументы в метод, и делать над ними действия.

def create_point(x, y, color: "white", size: 1, **h)
p [x, y, color, size, h]
end
create_point(2, 3, style: "solid", styler: "blue")
#=> [2, 3, "white", 1, {:style=>"solid", :styler=>"blue"}]


При создании методов с произвольным количеством аргументов следует обратить внимание на такое правило: аргумент реализующий прием множественного количества аргументов должен быть представлен последним в списке аргументов метода. Примеры:

def multisum(a,b=5,*c)
sum = c.inject{|sum, num| sum += num}
sum*a*b
end

multisum(5, *[1, 2, 3, 4, 5]) #=> 70


В метод на самом деле передается набор аргументов: 5,1,2,3,4,5, поэтому аргумент a получает значение 5, аргумент b получает значение 1, а аргумент с получает все остальные значения. При таком положении дел предопределенные аргументы становятся бесполезными, так как если метод получает произвольное количество параметров, то они могут быть переписаны.
Выход из ситуации простой: не использовать предопределенные аргументы и аргументы получающие коллекцию значений в одном методе. Произвольное количество значений вы можете изначально передавать в виде коллекции. Пример:

def multisum(arg)
sum = arg[:collection].inject{|sum, num| sum += num}
sum * arg[:a] * arg[:b]
end

multisum({a:5, b:1, collection:[2,3,4,5]}) #=> 70


Приемники методов

Метод принадлежащий объекту имеет приемник — объект, которому он принадлежит. Если метод вызывается без указания приемника, то это означает, что приемником является текущий объект,то есть тот, в контексте которого вызывается метод. Примеры:

class A
def self.hello
puts 'hello'
end
def bye
puts 'bye'
end
end

A.hello #hello

A.new.bye #bye


Приемник self указывает на текущий объект, то есть метод .hello является методом класса, а метод #bye — методом объекта.

Трюки с методами

В Ruby существует возможность вызывать методы не на прямую, а через метод #send передавая ему в качестве аргумента символ соответствующий имени метода, пример:

Вызов:

"hello".upcase #=> "HELLO"

…соответствует:

"hello".send(:upcase) #=> "HELLO"


Одной из сильных сторон Ruby является метапрограммирование, которое позволяет создавать методы непосредственно во время выполнения программы.
Существует несколько вариантов создания методов «на лету» при помощи .define_method в контексте класса, при помощи #define_singleton_method в контексте объекта и обработкой ошибок вызова несуществующего метода при помощи #method_missing. Примеры:

class A
define_method :hello do
puts 'hello'
end
end

A.new.hello # hello


a = A.new
a.define_singleton_method(:bye) do |name|
puts "Hello, #{name}"
end #=> #<Proc:0x86df814@(irb):18 (lambda)>

a.bye('Vladimir') #Hello, Vladimir


class A
def method_missing(name, arg, &block)
if name =~ /count_to/
(arg + 1).times {|n| puts n}
end
end
end

a = A.new #=> #<A:0x86a0ba0>

a.count_to(5)
#0
#1
#2
#3
#4
#5

.define_method и #define_singleton_method принимают имя метода в виде строки или символа и блок кода, который превращается в процедуру, которая вызывается в контексте объявляемого метода. .define_method и #define_singleton_method следует использовать только в случае реальной необходимости, когда, например, имя метода задается переменной.

Во всех остальных случаях следует использовать стандартный синтаксис объявления методов при помощи def.

#method_missing — используется для обработки ошибки NoMethodError, которая возвращается при попытке вызвать несуществующий метод. То есть #method_missing не объявляет методы, а обрабатывает ошибки. В контексте #method_missing вы можете на основании имени вызываемого метода обработать ошибку, то есть с имитировать существование метода или нескольких методов.

singleton методы — это индивидуальные методы объекта, которые хранятся в singleton классе объекта по той простой причине, что объекты не хранят методы.