Статьи

Руби Важные Методы Крюка

Снимок экрана 2015-04-26 10.59.14

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

Вышеупомянутые функции, а также некоторые другие интересные языковые аспекты делают 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"
end
rake helloHellotask :hello, :name do |t, args|
puts "Hello #{args.name}"
end
Сначала создайте простое рейк-задание:

 t

Если вы запустите эту задачу с помощью argsargs.name Давайте расширим эту задачу с граблями, чтобы она принимала аргумент (имя человека), и приветствуем этого человека:

 name

Это имя задачи, а hello Как видите, мы вызвали rake hello["Imran Latif"]
=> Hello Imran Latif
Rakemethod_missing
Запустите задачу rake, передав ей аргумент имени следующим образом:

 args

Давайте посмотрим, как Rake::TaskArgumentsRake::TaskArguments

Объект RakeRakeздесь . Этот класс отвечает за управление аргументами, переданными в задачу Rake. Просматривая код method_missing Итак, как method_missing Ответ на этот вопрос заключается в том, что def method_missing(sym, *args)
lookup(sym.to_sym)
end
method_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.