В этой статье я хочу объяснить delegate метод Ruby on Rails - это что, почему и как на практическом примере.

КАКИЕ?

Согласно API doc [1] - он предоставляет метод класса« делегат , позволяющий легко выставлять открытые методы содержащихся объектов как ваши собственные». И подпись метода следующая:

delegate(*methods, to: nil, prefix: nil, allow_nil: nil) общедоступный

Если используется, этот метод класса делегата будет предоставлять набор * методов, принадлежащих целевому объекту, указанному в к.

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

ПОЧЕМУ?

Закон Деметры [2] или принцип наименьшего знания - это руководство по разработке программного обеспечения, которое, согласно Википедии, кратко описывается следующим образом:

  • Каждый отряд должен иметь только ограниченные знания о других отрядах: только отряды, «тесно» связанные с текущим отрядом.
  • Каждый юнит должен разговаривать только со своими друзьями; не разговаривай с незнакомцами.
  • Говорите только со своими ближайшими друзьями.

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

КАК?

Теперь, что касается того, как это сделать, я рассмотрю набор примеров. Большинство примеров собраны из книги [3] - «Take My Money: Accepting Payments on the Web», но, конечно, отредактированы для этой статьи.

Предположим, у нас есть две модели ActiveRecord, связанные ассоциацией has_many, как показано ниже:

class Event < ActiveRecord::Base
  has_many :performances
end
class Performance < ActiveRecord::Base
  belongs_to :event
end

Запущу консоль rails и исследую члены этих двух моделей -

[3] pry (main) ›Event. column_names
=› [«id», «name», «description», «image_url», «created_at», «updated_at»]

[4] pry (main) ›Производительность. column_names
=› [«id», «event_id», «start_time», «end_time», «created_at», «updated_at»]

Теперь, чтобы получить доступ к имени и описанию объекта Event из объекта Performance ,, мы обычно можем сделать следующее:

performance = Performance.find (459352183)

[8] pry (main) ›performance. event.name
=› «Bleacher Bums»

[9] pry (main) ›перформанс. event.description
=› «Спектакль о бездельниках на Bleacher»

Итак, мы находимся здесь через ассоциацию : событие.

Но если мы используем метод delegate, мы сможем более удобно использовать методы name и description. Давайте изменим для этого нашу модель Производительность.

class Performance < ApplicationRecord
  belongs_to :event
  
  delegate :name, :description, to: :event
end

Теперь я показал методы name и description объекта event, как если бы они принадлежали объекту performance. Из консоли rails теперь мы можем получить доступ к названию и описанию события, как показано ниже:

[10] pry (main) ›performance.name
=› «Bleacher Bums»

[11] pry (main) ›performance.description
=› «Игра про бездельников с отбеливателем»

Нам больше не нужно использовать связь : событие.

Но что, если бы сама модель производительности имела метод имени? Здесь в игру вступает аргумент префикс. В этом случае я могу изменить модель Производительность -

class Performance < ApplicationRecord 
  belongs_to :event

  delegate :name, :description, to: :event, prefix: true
  def name
    "performance"
  end
end

Если мы укажем prefix: true, мы сможем получить доступ к имени производительности и имени события, как показано ниже:

[21] pry (main) ›перформанс. name
=› «перформанс»

[22] pry (main) ›перформанс .event_name
=› «Bleacher Bums»

Я могу пойти еще дальше, чтобы указать префикс, как мы хотим, как показано ниже -

class Performance < ApplicationRecord
  belongs_to :event
  
  delegate :name, :description, to: :event, prefix: :show
end

С указанным префиксом мы можем получить доступ к имени и описанию события, как показано ниже:

[25] pry (main) ›перформанс. show _name
=› «Bleacher Bums»

[26] pry (main) ›перформанс. show _description
=› «Спектакль о бездельниках Bleacher»

В случае, если объект, указанный с помощью до, равен nil, он вызовет исключение, как указано ниже:

class Performance < ApplicationRecord
 
  belongs_to :event

  delegate :name, :description, to: :event
end

[33] pry (main) ›Performance.new.event
=› ноль

[34] pry (main) ›Performance.new.name
Module :: DelegationError: Performance # name делегировано event.name, но событие равно нулю: #‹ Performance id: nil, event_id: nil, start_time: nil, end_time: nil, created_at: nil, updated_at: nil ›

Я получил Module :: DelegationError. Чтобы подавить эту ошибку и вместо этого получить ответ nil, я могу использовать аргумент allow_nil, как показано ниже:

class Performance < ApplicationRecord
 
  belongs_to :event
  
  delegate :name, :description, to: :event, allow_nil: true
end

[36] pry (main) ›Performance.new.name
=› ноль

В качестве побочного примечания, если принимающий объект не nil, но метод не существует, мы получим NoMethodError, как показано ниже:

class Performance < ApplicationRecord

  belongs_to :event

  delegate :name, :description, :time, to: :event, allow_nil: true
end

[43] pry (main) ›performance. time
NoMethodError: неопределенный метод` time ’для #‹ Event: 0x007fd776780730 ›

Более практичный пример

Приведенный ниже отрывок также из книги [3] -

class StripeToken

  attr_accessor :credit_card_number, :expiration_month, :expiration_year, :cvc

  def initialize(credit_card_number:, expiration_month:, expiration_year:, cvc:)
    @credit_card_number = credit_card_number
    @expiration_month = expiration_month
    @expiration_year = expiration_year
    @cvc = cvc
  end

  def token
    @token ||= Stripe::Token.create(
      card: {
        number: credit_card_number, exp_month: expiration_month,
        exp_year: expiration_year, cvc: cvc})
  end

  delegate :id, to: :token

  def to_s
    "STRIPE TOKEN: #{id}"
  end

  def inspect
    "STRIPE TOKEN #{id}"
  end
end

Здесь я пытаюсь создать объект токена Stripe. Команда делегата преобразует id в token.id. Давайте попробуем покрутить консоль на рельсах -

[102] pry (main) ›StripeToken.new (номер_кредитной_карты:« 424242424242 », expiration_month: 12, expiration_year: 2020, cvc: 111) .id
=› «tok_1GMRoBBN4gTWM2EPi6odYKKA»

Делегирование id дает удобный способ прямого доступа к идентификатору токена. Как указано в приведенном выше коде, я могу легко определить удобные методы to_s и inspect с помощью делегирования id. С рельсовой консоли -

[103] pry (main) ›StripeToken.new (номер_кредитной_карты:« 424242424242 », expiration_month: 12, expiration_year: 2020, cvc: 111). to_s
=› «STRIPE TOKEN: tok_1GMRqEBN4gTwfuD2 ”

[104] pry (main) ›StripeToken.new (номер_кредитной_карты:« 424242424242 », expiration_month: 12, expiration_year: 2020, cvc: 111). проверить
=› «STRIPE TOKEN tok_1GMRqKBN4gTWM2EPoQB»

В этой статье я попытался разъяснить метод делегирования Рейла и то, как читатели могут применить его на практике. Надеюсь, кому-то это понравилось :)

Чтобы получить более подробные и подробные технические сообщения в будущем, подпишитесь на меня здесь или в twitter.

Использованная литература:

  1. Https://apidock.com/rails/Module/delegatehttps://apidock.com/rails/Module/delegate
  2. Http://en.wikipedia.org/wiki/Law_of_Demeter
  3. Https://www.amazon.com/Take-My-Money-Accepting-Payments/dp/1680501992