В этой статье я хочу объяснить 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.