The hook method in Ruby _ruby topics

Source: Internet
Author: User
Tags hash instance method


Ruby's philosophy is based on a basic element that makes programmers happy. Ruby is very focused on the happiness of programmers and offers a number of different ways to implement it. Its metaprogramming capabilities enable programmers to write code that is generated dynamically at run time. Its threading capabilities allow programmers to have an elegant way of writing multithreaded code. Its hook method allows programmers to extend the behavior of a program while it is running.



These features, and some other cool language aspects, make Ruby one of the first choices to write code. This article explores some of the important hook methods in Ruby. We will discuss the hook methods in different ways, such as what they are, what they are used for, and how we use them to solve different problems. We also have a look at some popular Ruby frameworks/gem how packages/libraries use them to provide very cool features.



Here we go.



What is a hook method?



The hook method provides a way to extend the behavior of a program while the program is running. Suppose you have such a feature that you can receive a notification whenever a subclass inherits some particular parent class, or it is more graceful to handle a method that is not callable on an object rather than having the compiler throw an exception. These situations are the use of hook methods, but their usage is not limited to this. Different frames/libraries use different hook methods to implement their functions.



In this article we will discuss the following hook methods:



1.included
2.extended
3.prepended
4.inherited
5.method_missing



Included



Ruby gives us a way to write modular code for other modules/classes using modules (modules) (called Mixed classes (mixins) in other languages). The concept of a module is simple, it is a separate block of code that can be used elsewhere.



For example, if we want to write some code that calls a particular method at any time, it returns a static string. Let's call this method name. You may also want to use the same code elsewhere. This is best done by creating a new module. Let's create a:


 code as follows:

Module person
def name
Puts "My name are person"
End
End





This is a very simple module, and only one name method is used to return a static string. Use this module in our program:





 code as follows:

Class User
Include person
End





Ruby offers a number of different ways to use modules. Include is one of them. What the include does is that the method defined within the module is available on the instance variable of one class. In our case, it is a way to change the method defined in the person module into a User class instance object. This is equivalent to writing the name method in the User class, but the benefits defined in module are reusable. To invoke the name method we need to create a User instance object and then invoke the name method on the object. For example:


 code as follows:

User.new.name
=> My name is person

Let's take a look at the hook method based on include. Included is a hook method provided by Ruby that is invoked when you include a module in some module or class. To update the person module:







 code as follows:

Module person
def self.included (Base)
Puts "#{base} included #{self}"
End


def name
"My name is person"
End
End





You can see a new method included is defined as the class method of the person module. This included method is invoked when you execute an include person in another module or class. One of the parameters that the method receives is a reference to the class that contains the module. Try running User.new.name and you will see the following output:





 code as follows:

User included person
My name was person

As you can see, base returns the class name that contains the module. Now that we have a reference to the class that contains the person module, we can implement the functionality we want through metaprogramming. Let's take a look at how devise uses included hooks.





The included in the Devise



Devise is one of the most widely used authentication gem packages in Ruby. It was developed primarily by my favorite programmer Josévalim and is now being maintained by some great contributors. Devise provides us with perfect features from registration to login, from forgetting passwords to retrieving passwords, and so on. It allows us to configure a variety of modules using simple syntax in the user model:





code as follows:

Devise:d atabase_authenticatable,: registerable, validatable





The Devise method used in our model is defined here. To make it easier for me to paste this code in the following section:


 code as follows:

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! Todo
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






The module names passed to the Devise method in our model are saved as an array in the *modules. Call extract_options! for the incoming module method to extract the options that might be passed in. Each method is called in 11 lines, and each module is represented in the code block by M. In line 12, M will be converted to a constant (class name), so use m.to.classify for example: validatable such a symbol becomes validatable. Casually speaking, classify is the Activesupport method.



Devise::models.const_get (m.to_classify) Gets a reference to the module and assigns it to the MoD. Include the module in line 27 using include mod. The Validatable module in the example is defined here. The validatable included hook method is defined as follows:


code as follows:

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:p Assword, if::p assword_required?
Validates_confirmation_of:p Assword, if::p assword_required?
Validates_length_of:p Assword, Within:password_length, allow_blank:true
End
End





At this point the model is base. The Class_eval code block on line 5th takes the class as the context for the evaluation operation. Code written through Class_eval is the same as pasting code into a file that opens the class directly. Devise is class_eval to include validation in our user model.



When we try to register with devise or log in, we see these validations, but we don't write the validation code. Devise is using the included hook to achieve this. It's very elegant.



Extended



Ruby also allows developers to extend (extend) a module, which is a bit different from the inclusion (include). Extend is a method that applies a method that is defined in a module to a class, rather than an instance. Let's take a look at a simple example:


 code as follows:

Module person
def name
"My name is person"
End
End


Class User
Extend person
End


Puts User.Name # => my name's person
As you can see, we call the name method defined within the person module as the class method of User. Extend adds a method within the person module to the User class. Extend can also be used to use the method within a module as a single instance method (singleton methods). Let's look at another example:
 code as follows:

# We are using same person module and the User class from previous example.


u1 = User.new
U2 = User.new

U1.extend person

Puts U1.name # => my name's person
Puts U2.name # => undefined method ' name ' for #<user:0x007fb8aaa2ab38> (Nomethoderror)


We created two User instance objects and raised the person as a parameter in the U1 with the Extend method. Using this invocation method, the person's name method is valid only for U1, and is not valid for other instances.





As with included, the hook method corresponding to extend is extended. This method is invoked when a module performs a extend operation by another module or class. Let's take a look at an example:


 code as follows:

# 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






The result of this code is the output User extended person.



The introduction of extended is over, let's see how ActiveRecord uses it.



The extended in the ActiveRecord



ActiveRecord is an ORM framework that is widely used in Ruby and Rails. It has many cool features, so using it is the first choice for ORM in many cases. Let's go inside the ActiveRecord and see how ActiveRecord uses callbacks. (We are using Rails v3.2.21)



ActiveRecord here Extend the Activerecord::models module.


 code as follows:

Extend Activemodel::callbacks

Activemodel provides a set of interfaces that are used in the model class. They allow actionpack to interact with models that are not ActiveRecord. Here, inside Activemodel::callbacks you will see the following code:
code as follows:

def self.extended (Base)
Base.class_eval do
Include Activesupport::callbacks
End
End





Activemodel::callbacks called the Class_eval method on base that is Activerecord::callbacks, and contains the Activesupport::callbacks module. As we mentioned earlier, it is the same to call Class_eval on a class and manually write the code in this class. Activesupport::callbacks provides Activerecord::callbacks with a callback method in Rails.



Here we discuss the Extend method and the corresponding hook extended. And also learned how Activerecord/activemodel uses the above methods to provide us with the available functionality.



prepended



Another way to use the method defined inside the module is called Prepend. Prepend is introduced in Ruby 2.0 and is very different from include and extend. Methods introduced with include and extend can be overridden by a target module/class redefinition. For example, if we define a method named name in a module, and also define a method with the same name in the target module/class. So the name method defined in our class will overwrite the in the module. And prepend is not the same, it will cover the methods introduced in the Prepend module/class. Let's take a look at a simple example:





 code as follows:

Module person
def name
"My name belongs to Person"
End
End


Class User
Include person
def name
"My name belongs to User"
End
End

Puts User.new.name
=> my name belongs to User





Now let's look at the prepend:




 code as follows:


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





Using prepend person overrides the same method in User, so the result for the terminal output is my name belongs to person. Prepend is actually adding a method to the front of the method chain. When the name method defined within the User class is invoked, super is invoked to invoke the name of the person module.



The callback name corresponding to the prepend is (you should have guessed) prepended. It is invoked when a module is preset to another module/class. Let's look at the effect. To update the definition of the person module:


 code as follows:

Module person
def self.prepended (Base)
Puts "#{self} prepended to #{base}"
End


def name
"My name belongs to Person"
End
End


You should see the following results when you run this code:







 code as follows:

Person prepended to User
My name belongs to person





Prepend was introduced to remove the ugliness of the Alias_method_chain hack, which was used extensively by rails and other libraries to achieve the same functions as prepend. Because Prepend is only available in the Ruby >= 2.0 version, you should upgrade your Ruby version if you plan to use prepend.



Inherited



Inheritance is one of the most important concepts in object-oriented objects. Ruby is an object-oriented programming language and provides the ability to inherit a subclass from a base/parent class. Let's look at a simple example:


code as follows:

Class Person
def name
"My name is person"
End
End


Class User < person
End

Puts User.new.name # => my name's person





We created a person class and a child class User. The method defined in person also becomes part of the User. This is very simple to inherit. You may be curious, is there any way to be notified when a class inherits from another class? Yes, Ruby has a hook called inherited that can be implemented. Let's take another look at this example:





code as follows:

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





As you can see, the inherited class method will be invoked when the person class is inherited by other subclasses. The results of running the above code are as follows:





 code as follows:

User Inherits Person
My name was person





Let's take a look at how Rails uses inherited in its code.



The inherited in Rails



The rails application has an important class named application, defined within the Config/application.rb file. This class performs a number of different tasks, such as running all railties, engines, and initialization of plug-ins. An interesting event about the application class is that two instances cannot be run in the same process. If we try to modify this behavior, rails throws an exception. Let's see how rails implements this feature.



The application class inherits from the Rails::application, which is defined here. The inherited hook is defined in line 62, and it is invoked when our Rails application application class inherits Rails::application. The code for the inherited hook is as follows:


 code as follows:

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 << Self is another way to define class methods in Ruby. The 1th line in inherited is to check if the rails.application already exists. Throws an exception if it exists. The first time this code is run, Rails.application returns false and then calls Super. Here super is the rails::engine inherited hook, because Rails::application inherits from Rails::engine.



On the next line, you will see that Rails.application is assigned to Base.instance. The rest is to set up the rails application.



This is how rails skillfully uses inherited hooks to implement a single instance of our Rails application class.



Method_missing



Method_missing may be the most widely used hook in Ruby. It is used in many popular Ruby frameworks/gem packages/libraries. This hook method is invoked when we attempt to access a method that does not exist on an object. Let's take a look at a simple example:





 code as follows:

Class Person
def name
"My name is person"
End
End


p = person.new

Puts P.name # => my name's person
Puts P.address # => undefined method "Address" for #<person:0x007fb730a2b450> (Nomethoderror)


We define a simple person class, which has only one name method. It then creates an instance object of person and invokes the name and address two methods, respectively. Because name is defined in person, this operation is fine. However, the person does not define an address, which throws an exception. Method_missing Hooks can gracefully capture these undefined methods and avoid such exceptions. Let's revise the person class:







 code as follows:

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's person
Puts P.address # => address isn't defined on #<person:0x007fb2bb022fe0>


method_missing receives two parameters: the name of the method being invoked and the arguments passed to the method. First Ruby will look for the method we are trying to invoke, and if the method is not found, it will look for the Method_missing method. Now we overload the method_missing in person, so Ruby will call it instead of throwing an exception.





Let's see how Rake uses method_missing.



The Method_missing in Rake



Rake is one of the most widely used gem packages in Ruby. Rake uses method_missing to provide access to the arguments passed to the rake task. Start by creating a simple rake task:





 code as follows:

Task:hello do
Puts "Hello"
End

If you perform this task by calling rake Hello, you will see the output hello. Let's extend this rake task to receive a parameter (a person name) and greet him:
 code as follows:

Task:hello,: Name do |t, args|
Puts "Hello #{args.name}"
End

T is the name of the task, and args saves the arguments passed over. As you can see, we call Args.name to get the name parameter passed to the Hello task. Run the task and pass a parameter:







code as follows:

Rake hello["Imran Latif"]
=> Hello Imran Latif

Let's see how Rake uses method_missing to provide us with the parameters that are passed to the task.





The Args object in the above task is a rake::taskarguments instance, which is defined here. This class is responsible for managing the parameters passed to the rake task. Looking at the Rake::taskarguments code, you will find that there is no defined method to pass the parameters to the task. So how does Rake provide the parameters to the task? The answer is that Rake uses the method_missing to achieve this function cleverly. Look at the definition of line 64th method_missing:


code as follows:

def method_missing (Sym, *args)
Lookup (SYM.TO_SYM)
End

Defining method_missing in this class is to ensure access to those undefined methods, rather than throwing exceptions by Ruby. In method_missing, it calls the lookup method:
 code as follows:

Def lookup (name)
If @hash. Has_key? (name)
@hash [Name]
elsif @parent
@parent. Lookup (name)
End
End





Method_missing invokes lookup and passes the method name in symbol (symbol) to it. The lookup method is found in the @hash, which is created in the Rake::taskarguments constructor. If the parameter is included in the @hash, it returns, and if not in the @hash, Rake attempts to invoke @parent lookup. If the parameter is not found, nothing is returned.



This is how Rake skillfully uses method_missing to provide access to the arguments passed to the Rake task. Thanks Jim Weirich for writing rake.



Conclusion



We discussed 5 important ruby hook methods, explored how they work, and how the popular framework/gem packages use them to provide some elegant functionality. I hope you will like this article. Tell us about your favorite ruby hooks in the comments and the problems you use to solve them.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.