Философия Руби основана на сильном примитиве, который является счастьем программиста . Руби твердо верит в счастье программиста и предоставила много разных способов сделать это. Его возможности метапрограммирования позволяют программистам писать динамический код во время выполнения. Его многопоточные возможности дают программистам элегантный способ написания многопоточного кода. Методы хуков помогают программистам расширять поведение программ во время выполнения.
Вышеупомянутые функции, а также некоторые другие интересные языковые аспекты делают Ruby одним из предпочтительных вариантов написания кода. В этом посте будут рассмотрены некоторые важные методы хуков в Ruby. Мы обсудим различные аспекты методов подключения, например, для чего они используются, и как мы можем использовать их для решения различных проблем. Мы также рассмотрим, как популярные фреймворки / гемы / библиотеки Ruby используют их для предоставления довольно интересных функций.
Давайте начнем.
Что такое метод Крюка?
Методы хука предоставляют способ расширить поведение программ во время выполнения. Представьте себе возможность получать уведомления всякий раз, когда дочерний класс наследует от некоторого определенного родительского класса, или элегантно обрабатывать не вызываемые методы для объектов, не позволяя компилятору вызывать исключения. Вот некоторые из вариантов использования для методов ловушки, но их использование не ограничено этим. Различные платформы / библиотеки использовали разные методы подключения для достижения желаемой функциональности.
Мы будем обсуждать следующие методы ловушек в этом посте:
included-
extended -
prepended -
inherited -
method_missing
included
Ruby предоставляет нам способ написания модульного кода с использованием modulesmixinsmodulesclasses Идея module это отдельный фрагмент кода, который можно использовать в других местах.
Например, если мы хотим написать некоторый код, который возвращает статическую строку всякий раз, когда вызывается определенный метод. Давайте назовем это name Возможно, вы захотите использовать этот же фрагмент кода и в других местах. Создание module Давайте создадим один:
module Person
def name
puts "My name is Person"
end
end
Это довольно простой модуль, в котором только одно name Давайте использовать это в нашей программе:
class User
include Person
end
Ruby предоставляет несколько разных способов использования modules Одним из них является include Что делает includemoduleclass В нашем случае методы, определенные в модуле PersonUser Это как если бы мы написали метод nameUsermodule Для вызова nameUsername Например:
User.new.name
=> My name is Person
Давайте посмотрим на метод ловушки, основанный на include includedincludemodulemoduleclass Обновите модуль Person
module Person
def self.included(base)
puts "#{base} included #{self}"
end
def name
"My name is Person"
end
end
Вы можете увидеть новый includedclassPerson Этот includedincludePerson Этот метод получает аргумент, который является ссылкой на класс, включая модуль. Попробуйте запустить User.new.name
User included Person
My name is Person
Как видите, base Теперь, когда у нас есть ссылка на класс, включая Person Давайте посмотрим, как Devise использует included
included
Devise является одним из наиболее широко используемых жетонов аутентификации в Ruby. Он в основном написан моим любимым программистом, Хосе Валимом , и теперь поддерживается некоторыми замечательными авторами. Devise предоставляет нам полную функциональность от регистрации до входа в систему, от забытого пароля для восстановления пароля и т. Д. Он позволяет нам настраивать различные модули, используя простой синтаксис в пользовательской модели:
devise :database_authenticatable, :registerable, :validatable
Метод devisemodelsздесь . Я вставил код ниже для вашего удобства:
def devise(*modules)
options = modules.extract_options!.dup
selected_modules = modules.map(&:to_sym).uniq.sort_by do |s|
Devise::ALL.index(s) || -1 # follow Devise::ALL order
end
devise_modules_hook! do
include Devise::Models::Authenticatable
selected_modules.each do |m|
mod = Devise::Models.const_get(m.to_s.classify)
if mod.const_defined?("ClassMethods")
class_mod = mod.const_get("ClassMethods")
extend class_mod
if class_mod.respond_to?(:available_configs)
available_configs = class_mod.available_configs
available_configs.each do |config|
next unless options.key?(config)
send(:"#{config}=", options.delete(config))
end
end
end
include mod
end
self.devise_modules |= selected_modules
options.each { |key, value| send(:"#{key}=", value) }
end
end
Именованные модули, переданные методу devise*modules extract_options! вызывается на переданных модулях для извлечения любых опций, которые мог передать этот пользователь. В строке 11 находится каждый модуль, представленный как each В строке 12 mm:validatableValidatablem.to.classify кстати, classify Devise::Models.const_get(m.to_classify)mod В строке 27 ссылка на модуль включена с использованием include mod В примере Validatableздесь , included Validatableincluded
def self.included(base)
base.extend ClassMethods
assert_validations_api!(base)
base.class_eval do
validates_presence_of :email, if: :email_required?
validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
validates_length_of :password, within: password_length, allow_blank: true
end
end
Модель в данном случае является base В строке 5 находится блок class_eval Написание кода через class_eval Devise использует class_evalincluded
Мы видим проверки, когда пытаемся зарегистрироваться или войти, используя Devise, но мы не писали эти проверки. Devise предоставляет их, используя преимущества extend Довольно аккуратно.
расширенный
Ruby также позволяет разработчикам includemodule Вместо применения методов, определенных в extendmodule Person
def name
"My name is Person"
end
end
class User
extend Person
end
puts User.name # => My name is Person Давайте посмотрим на быстрый пример:
name
Как видите, мы назвали метод PersonUserextend PersonUserextend # We are using same Person module and User class from previous example.
u1 = User.new
u2 = User.new
u1.extend Person
puts u1.name # => My name is Person
puts u2.name # => undefined method `name' for #<User:0x007fb8aaa2ab38> (NoMethodError) Давайте посмотрим еще один быстрый пример:
User
Мы создали два экземпляра extendu1Personname Из-за этого вызова метод Personu1included
Так же, как и в extendedextendextend Он вызывается, когда модуль # Modified version of Person module
module Person
def self.extended(base)
puts "#{base} extended #{self}"
end
def name
"My name is Person"
end
end
class User
extend Person
end Давайте посмотрим на пример:
User extended Person
Выполнение этого кода приводит к extended
С введением в ActiveRecordextended
ActiveRecord
ActiveRecord Он имеет много интересных функций, что делает его предпочтительным ORM в большинстве случаев. Давайте погрузимся во внутренности ActiveRecordActiveRecord
extendActiveRecord::Modelsextend ActiveModel::Callbacksздесь .
ActiveModel
ActiveModel::Callbacks Они позволяют помощникам ActionPack взаимодействовать с не-ActiveRecord моделями. В def self.extended(base)здесь , вы увидите следующий код:
base.class_eval do
include ActiveSupport::Callbacks
end
end
ActiveModel::Callbacks
class_evalbaseActiveRecord::CallbacksActiveSupport::Callbacksclass_eval Как мы уже говорили ранее, вызов ActiveSupport::Callbacks Это ActiveRecord::Callbacksextend
Мы обсудили extendedActiveRecordActiveModel
указывая префикс
Есть еще один способ использовать методы, определенные в модулях, называемые prepend prependincludeextend Методы, используемые includeextend Например, если мы определили namename prependprepend Давайте посмотрим на быстрый пример:
module Person
def name
"My name belongs to Person"
end
end
class User
def name
"My name belongs to User"
end
end
puts User.new.name
=> My name belongs to User
Теперь посмотрим, что prepend
module Person
def name
"My name belongs to Person"
end
end
class User
prepend Person
def name
"My name belongs to User"
end
end
puts User.new.name
=> My name belongs to Person
Добавление prepend PersonUserMy name belongs to Personprepend nameдобавляет методы к цепочке методов. Для вызова метода UsersupernamePersonprepend
prependedPersonдобавляется к другому модулю / классу. Давайте посмотрим, что в действии. Обновите определение модуля module Person
def self.prepended(base)
puts "#{self} prepended to #{base}"
end
def name
"My name belongs to Person"
end
end
Person prepended to User
My name belongs to Person
Как только вы запустите этот код, вы увидите следующее:
prepend
alias_method_chainprependprepend Поскольку prependclass Person
def name
"My name is Person"
end
end
class User < Person
end
puts User.new.name # => My name is Person
унаследованный
Наследование является одним из наиболее важных понятий в объектно-ориентированном программировании. Ruby является объектно-ориентированным языком и предоставляет возможность наследовать дочерний класс от некоторого базового / родительского класса. Давайте посмотрим на быстрый пример:
Person
Мы создали класс UserPerson Методы, определенные в Userinherited Это довольно прямое наследство. Вы можете быть удивлены, есть ли способ получить уведомление, когда класс наследует от другого класса? Да, для этой конкретной вещи есть class Person
def self.inherited(child_class)
puts "#{child_class} inherits #{self}"
end
def name
"My name is Person"
end
end
class User < Person
end
puts User.new.name Давайте посмотрим, что в действии:
inherited
Как видите, PersonUser inherits Person Выполнение приведенного выше фрагмента кода показывает:
My name is Person
Rails
Давайте посмотрим, как inheritedApplication
наследуется в Rails
В каждом приложении Rails есть важный класс, называемый Applicationconfig / application.rb . Этот класс выполняет много разных задач, таких как выполнение всех Railties, движков и инициализаторов плагинов. Интересной особенностью класса Application Если мы попытаемся переопределить это поведение, Rails выдаст исключение. Давайте посмотрим, как Rails реализовал эту функцию.
Класс Rails::Applicationinheritedздесь . В строке 62 определен ApplicationRails::Application Код inherited
class << self
def inherited(base)
raise "You cannot have more than one Rails::Application" if Rails.application
super
Rails.application = base.instance
Rails.application.add_lib_to_load_path!
ActiveSupport.run_load_hooks(:before_configuration, base.instance)
end
end
class is another way of defining class methods in Ruby. In inheritedRails.applicationRails.applicationsupersuper При первом inheritedRails::EngineRails::Application Rails::EngineRails.applicationbase.instanceinheritedApplication
На следующей строке вы увидите, что method_missingclass Person
def name
"My name is Person"
end
end
p = Person.new
puts p.name # => My name is Person
puts p.address # => undefined method `address' for #<Person:0x007fb730a2b450> (NoMethodError) Оставшаяся часть метода устанавливает приложение Rails.
Именно так Rails разумно использует Personname
method_missing
Person Его можно найти во многих популярных фреймворках / гемах / библиотеках Ruby. Он вызывается, когда наш код пытается вызвать несуществующий метод объекта. Давайте посмотрим на быстрый пример:
name
Мы объявили простой класс addressname Затем создайте экземпляр PersonaddressPerson Поскольку method_missingPerson Однако class Person
def method_missing(sym, *args)
"#{sym} not defined on #{self}"
end
def name
"My name is Person"
end
end
p = Person.new
puts p.name # => My name is Person
puts p.address # => address not defined on #<Person:0x007fb2bb022fe0>method_missing method_missing Давайте напишем новую версию класса method_missing
Person
Rake Во-первых, Ruby будет искать метод, который мы пытаемся вызвать, если метод не найден, он будет искать method_missing Теперь мы переопределили RakeRake
Давайте посмотрим, как гем method_missingarguments
method_missing в Rake
task :hello do
puts "Hello"
endrake helloHellotask :hello, :name do |t, args| Сначала создайте простое рейк-задание:
puts "Hello #{args.name}"
end
t
Если вы запустите эту задачу с помощью argsargs.name Давайте расширим эту задачу с граблями, чтобы она принимала аргумент (имя человека), и приветствуем этого человека:
name
Это имя задачи, а hello Как видите, мы вызвали rake hello["Imran Latif"]
=> Hello Imran LatifRakemethod_missing Запустите задачу rake, передав ей аргумент имени следующим образом:
args
Давайте посмотрим, как Rake::TaskArgumentsRake::TaskArguments
Объект RakeRakeздесь . Этот класс отвечает за управление аргументами, переданными в задачу Rake. Просматривая код method_missing Итак, как method_missing Ответ на этот вопрос заключается в том, что def method_missing(sym, *args)
lookup(sym.to_sym)
endmethod_missingmethod_missing Если вы посмотрите на строку 64, lookup
def lookup(name)
if @hash.has_key?(name)
@hash[name]
elsif @parent
@parent.lookup(name)
end
end
Наличие method_missing В этом lookupSymbol
lookup
@hashRake::TaskArguments@hash Метод @hashRakelookup Если @parentRakemethod_missingenddo … end Если аргумент не найден, то ничего не возвращается.
Вот как {}bundle install Спасибо Джиму Вейриху за написание Рейка.
Заключительные замечания
Мы обсудили пять важных методов подключения Ruby, изучили, как они работают, и как их используют популярные фреймворки / гемы, чтобы обеспечить некоторую полезную функциональность. Я надеюсь, вам понравилась эта статья. Пожалуйста, дайте нам знать в комментариях о вашем любимом хуке Ruby и о том, что вы решили с помощью этого хука Ruby.
