Философия Руби основана на сильном примитиве, который является счастьем программиста . Руби твердо верит в счастье программиста и предоставила много разных способов сделать это. Его возможности метапрограммирования позволяют программистам писать динамический код во время выполнения. Его многопоточные возможности дают программистам элегантный способ написания многопоточного кода. Методы хуков помогают программистам расширять поведение программ во время выполнения.
Вышеупомянутые функции, а также некоторые другие интересные языковые аспекты делают Ruby одним из предпочтительных вариантов написания кода. В этом посте будут рассмотрены некоторые важные методы хуков в Ruby. Мы обсудим различные аспекты методов подключения, например, для чего они используются, и как мы можем использовать их для решения различных проблем. Мы также рассмотрим, как популярные фреймворки / гемы / библиотеки Ruby используют их для предоставления довольно интересных функций.
Давайте начнем.
Что такое метод Крюка?
Методы хука предоставляют способ расширить поведение программ во время выполнения. Представьте себе возможность получать уведомления всякий раз, когда дочерний класс наследует от некоторого определенного родительского класса, или элегантно обрабатывать не вызываемые методы для объектов, не позволяя компилятору вызывать исключения. Вот некоторые из вариантов использования для методов ловушки, но их использование не ограничено этим. Различные платформы / библиотеки использовали разные методы подключения для достижения желаемой функциональности.
Мы будем обсуждать следующие методы ловушек в этом посте:
included
-
extended
-
prepended
-
inherited
-
method_missing
included
Ruby предоставляет нам способ написания модульного кода с использованием modules
mixins
modules
classes
Идея module
это отдельный фрагмент кода, который можно использовать в других местах.
Например, если мы хотим написать некоторый код, который возвращает статическую строку всякий раз, когда вызывается определенный метод. Давайте назовем это name
Возможно, вы захотите использовать этот же фрагмент кода и в других местах. Создание module
Давайте создадим один:
module Person
def name
puts "My name is Person"
end
end
Это довольно простой модуль, в котором только одно name
Давайте использовать это в нашей программе:
class User
include Person
end
Ruby предоставляет несколько разных способов использования modules
Одним из них является include
Что делает include
module
class
В нашем случае методы, определенные в модуле Person
User
Это как если бы мы написали метод name
User
module
Для вызова name
User
name
Например:
User.new.name
=> My name is Person
Давайте посмотрим на метод ловушки, основанный на include
included
include
module
module
class
Обновите модуль Person
module Person
def self.included(base)
puts "#{base} included #{self}"
end
def name
"My name is Person"
end
end
Вы можете увидеть новый included
class
Person
Этот included
includePerson
Этот метод получает аргумент, который является ссылкой на класс, включая модуль. Попробуйте запустить User.new.name
User included Person
My name is Person
Как видите, base
Теперь, когда у нас есть ссылка на класс, включая Person
Давайте посмотрим, как Devise использует included
included
Devise является одним из наиболее широко используемых жетонов аутентификации в Ruby. Он в основном написан моим любимым программистом, Хосе Валимом , и теперь поддерживается некоторыми замечательными авторами. Devise предоставляет нам полную функциональность от регистрации до входа в систему, от забытого пароля для восстановления пароля и т. Д. Он позволяет нам настраивать различные модули, используя простой синтаксис в пользовательской модели:
devise :database_authenticatable, :registerable, :validatable
Метод devise
models
здесь . Я вставил код ниже для вашего удобства:
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 m
m
:validatable
Validatable
m.to.classify
кстати, classify
Devise::Models.const_get(m.to_classify)
mod
В строке 27 ссылка на модуль включена с использованием include mod
В примере Validatable
здесь , included
Validatable
included
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_eval
included
Мы видим проверки, когда пытаемся зарегистрироваться или войти, используя Devise, но мы не писали эти проверки. Devise предоставляет их, используя преимущества extend
Довольно аккуратно.
расширенный
Ruby также позволяет разработчикам include
module
Вместо применения методов, определенных в extend
module Person
def name
"My name is Person"
end
end
class User
extend Person
end
puts User.name # => My name is Person Давайте посмотрим на быстрый пример:
name
Как видите, мы назвали метод Person
User
extend
Person
User
extend
# 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
Мы создали два экземпляра extend
u1
Person
name
Из-за этого вызова метод Person
u1
included
Так же, как и в extended
extend
extend
Он вызывается, когда модуль # 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
С введением в ActiveRecord
extended
ActiveRecord
ActiveRecord
Он имеет много интересных функций, что делает его предпочтительным ORM в большинстве случаев. Давайте погрузимся во внутренности ActiveRecord
ActiveRecord
extend
ActiveRecord::Models
extend ActiveModel::Callbacks
здесь .
ActiveModel
ActiveModel::Callbacks
Они позволяют помощникам ActionPack взаимодействовать с не-ActiveRecord моделями. В def self.extended(base)
здесь , вы увидите следующий код:
base.class_eval do
include ActiveSupport::Callbacks
end
end
ActiveModel::Callbacks
class_eval
base
ActiveRecord::Callbacks
ActiveSupport::Callbacks
class_eval
Как мы уже говорили ранее, вызов ActiveSupport::Callbacks
Это ActiveRecord::Callbacks
extend
Мы обсудили extended
ActiveRecord
ActiveModel
указывая префикс
Есть еще один способ использовать методы, определенные в модулях, называемые prepend
prepend
include
extend
Методы, используемые include
extend
Например, если мы определили name
name
prepend
prepend
Давайте посмотрим на быстрый пример:
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 Person
User
My name belongs to Person
prepend
name
добавляет методы к цепочке методов. Для вызова метода User
super
name
Person
prepend
prepended
Person
добавляется к другому модулю / классу. Давайте посмотрим, что в действии. Обновите определение модуля 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_chain
prepend
prepend
Поскольку prepend
class Person
def name
"My name is Person"
end
end
class User < Person
end
puts User.new.name # => My name is Person
унаследованный
Наследование является одним из наиболее важных понятий в объектно-ориентированном программировании. Ruby является объектно-ориентированным языком и предоставляет возможность наследовать дочерний класс от некоторого базового / родительского класса. Давайте посмотрим на быстрый пример:
Person
Мы создали класс User
Person
Методы, определенные в User
inherited
Это довольно прямое наследство. Вы можете быть удивлены, есть ли способ получить уведомление, когда класс наследует от другого класса? Да, для этой конкретной вещи есть 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
Как видите, Person
User inherits Person
Выполнение приведенного выше фрагмента кода показывает:
My name is Person
Rails
Давайте посмотрим, как inherited
Application
наследуется в Rails
В каждом приложении Rails есть важный класс, называемый Application
config / application.rb . Этот класс выполняет много разных задач, таких как выполнение всех Railties, движков и инициализаторов плагинов. Интересной особенностью класса Application
Если мы попытаемся переопределить это поведение, Rails выдаст исключение. Давайте посмотрим, как Rails реализовал эту функцию.
Класс Rails::Application
inherited
здесь . В строке 62 определен Application
Rails::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
inherited
Rails.application
Rails.application
super
super
При первом inherited
Rails::Engine
Rails::Application
Rails::Engine
Rails.application
base.instance
inherited
Application
На следующей строке вы увидите, что method_missing
class 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 разумно использует Person
name
method_missing
Person
Его можно найти во многих популярных фреймворках / гемах / библиотеках Ruby. Он вызывается, когда наш код пытается вызвать несуществующий метод объекта. Давайте посмотрим на быстрый пример:
name
Мы объявили простой класс address
name
Затем создайте экземпляр Person
address
Person
Поскольку method_missing
Person
Однако 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
Теперь мы переопределили Rake
Rake
Давайте посмотрим, как гем method_missing
arguments
method_missing в Rake
task :hello do
puts "Hello"
endrake hello
Hello
task :hello, :name do |t, args|
Сначала создайте простое рейк-задание:
puts "Hello #{args.name}"
end
t
Если вы запустите эту задачу с помощью args
args.name
Давайте расширим эту задачу с граблями, чтобы она принимала аргумент (имя человека), и приветствуем этого человека:
name
Это имя задачи, а hello
Как видите, мы вызвали rake hello["Imran Latif"]
=> Hello Imran LatifRake
method_missing
Запустите задачу rake, передав ей аргумент имени следующим образом:
args
Давайте посмотрим, как Rake::TaskArguments
Rake::TaskArguments
Объект Rake
Rake
здесь . Этот класс отвечает за управление аргументами, переданными в задачу Rake. Просматривая код method_missing
Итак, как method_missing
Ответ на этот вопрос заключается в том, что def method_missing(sym, *args)
lookup(sym.to_sym)
endmethod_missing
method_missing
Если вы посмотрите на строку 64, lookup
def lookup(name)
if @hash.has_key?(name)
@hash[name]
elsif @parent
@parent.lookup(name)
end
end
Наличие method_missing
В этом lookup
Symbol
lookup
@hash
Rake::TaskArguments
@hash
Метод @hash
Rake
lookup
Если @parent
Rake
method_missing
end
do … end
Если аргумент не найден, то ничего не возвращается.
Вот как {}
bundle install
Спасибо Джиму Вейриху за написание Рейка.
Заключительные замечания
Мы обсудили пять важных методов подключения Ruby, изучили, как они работают, и как их используют популярные фреймворки / гемы, чтобы обеспечить некоторую полезную функциональность. Я надеюсь, вам понравилась эта статья. Пожалуйста, дайте нам знать в комментариях о вашем любимом хуке Ruby и о том, что вы решили с помощью этого хука Ruby.