Ruby中钩子方法的运用实例解析
通过使用钩子方法,可以让我们在Ruby的类或模块的生命周期中进行干预,可以极大的提高编程的灵活性。
与生命周期相关的钩子方法有下面这些:
类与模块相关
- Class#inherited
- Module#include
- Module#prepended
- Module#extend_object
- Module#method_added
- Module#method_removed
- Module#method_undefined
单件类相关
- BasicObject#singleton_method_added
- BasicObject#singleton_method_removed
- BasicObject#singleton_method_undefined
示例代码
module M1 def self.included(othermod) puts “M1 was included into #{othermod}” end end module M2 def self.prepended(othermod) puts “M2 was prepended to #{othermod}” end end class C include M1 include M2 end # 输出 M1 was included into C M2 was prepended to C module M def self.method_added(method) puts “New method: M##{method}” end def my_method; end end # 输出 New method: M#my_method
除了上面列出来的一些方法外,也可以通过重写父类的某个方法,进行一些过滤操作后,再通过调用super方法完成原函数的功能,从而实现类似钩子方法的功效,如出一辙,环绕别名也可以作为一种钩子方法的替代实现。
运用实例
任务描述:
写一个操作方法类似attr_accessor的attr_checked的类宏,该类宏用来对属性值做检验,使用方法如下:
class Person include CheckedAttributes attr_checked :age do |v| v >= 18 end end me = Person.new me.age = 39 #ok me.age = 12 #抛出异常
实施计划:
使用eval方法编写一个名为add_checked_attribute的内核方法,为指定类添加经过简单校验的属性
重构add_checked_attribute方法,去掉eval方法,改用其它手段实现
添加代码块校验功能
修改add_checked_attribute为要求的attr_checked,并使其对所有类都可用
通过引入模块的方式,只对引入该功能模块的类添加attr_checked方法
Step 1
def add_checked_attribute(klass, attribute) eval " class #{klass} def #{attribute}=(value) raise 'Invalid attribute' unless value @#{attribute} = value end def #{attribute}() @#{attribute} end end " end add_checked_attribute(String, :my_attr) t = "hello,kitty" t.my_attr = 100 puts t.my_attr t.my_attr = false puts t.my_attr
这一步使用eval方法,用class和def关键词分别打开类,且定义了指定的属性的get和set方法,其中的set方法会简单的判断值是否为空(nil 或 false),如果是则抛出Invalid attribute异常。
Setp 2
def add_checked_attribute(klass, attribute) klass.class_eval do define_method "#{attribute}=" do |value| raise "Invaild attribute" unless value instance_variable_set("@#{attribute}", value) end define_method attribute do instance_variable_get "@#{attribute}" end end end
这一步更换掉了eval方法,同时也分别用class_eval和define_method方法替换了之前的class与def关键字,实例变量的设置和获取分别改用了instance_variable_set和instance_variable_get方法,使用上与第一步没有任何区别,只是一些内部实现的差异。
Step 3
def add_checked_attribute(klass, attribute, &validation) klass.class_eval do define_method "#{attribute}=" do |value| raise "Invaild attribute" unless validation.call(value) instance_variable_set("@#{attribute}", value) end define_method attribute do instance_variable_get "@#{attribute}" end end end add_checked_attribute(String, :my_attr){|v| v >= 180 } t = "hello,kitty" t.my_attr = 100 #Invaild attribute (RuntimeError) puts t.my_attr t.my_attr = 200 puts t.my_attr #200
没有什么奇特的,只是加了通过代码块验证,增加了校验的灵活性,不再仅仅局限于nil和false之间了。
Step 4
class Class def attr_checked(attribute, &validation) define_method "#{attribute}=" do |value| raise "Invaild attribute" unless validation.call(value) instance_variable_set("@#{attribute}", value) end define_method attribute do instance_variable_get "@#{attribute}" end end end String.add_checked(:my_attr){|v| v >= 180 } t = "hello,kitty" t.my_attr = 100 #Invaild attribute (RuntimeError) puts t.my_attr t.my_attr = 200 puts t.my_attr #200
这里我们把之前顶级作用域中方法名放到了Class中,由于所有对象都是Class的实例, 所以这里定义的实例方法,也能被Ruby中的其它所有类访问到,同时在class定义中,self就是当前类,所以也就省去了调用类这个参数和class_eval方法,并且我们把方法的名字也改成了attr_checked。
Step 5
module CheckedAttributes def self.included(base) base.extend ClassMethods end end module ClassMethods def attr_checked(attribute, &validation) define_method "#{attribute}=" do |value| raise "Invaild attribute" unless validation.call(value) instance_variable_set("@#{attribute}", value) end define_method attribute do instance_variable_get "@#{attribute}" end end end class Person include CheckedAttributes attr_checked :age do |v| v >= 18 end end
最后一步通过钩子方法,在CheckedAttributes模块被引入后,对当前类通过被引入模块进行扩展, 从而使当前类支持引入后的方法调用,即这里的get与set方法组。
到此,我们已经得到了一个名为attr_checked,类似attr_accessor的类宏,通过它你可以对属性进行你想要的校验。
相关文章
Ruby on Rails实现最基本的用户注册和登录功能的教程
这里我们主要以has_secure_password的用户密码验证功能为中心,来讲解Ruby on Rails实现最基本的用户注册和登录功能的教程,需要的朋友可以参考下...2020-06-30- 二分查找是一种在已经过排序的数组中搜索指定元素用的算法,这里我们就来看一下Ruby实现二分搜索(二分查找)算法的简单示例:...2020-06-30
- Ruby将字符串像数字一样处理.我们用单引号('...')或双引号("...")将它们括起来. ruby> "abc" "abc" ruby> 'abc' "abc" 单引号和双引号在某些情况下有不同的...2020-06-30
- Rack是一个连接Ruby程序与服务器程序之间的中间件,甚至可以说Rails也是在Rack的基础上建立起来的,这里我们就来为大家带来Ruby on Rails中Rack中间件的基础学习教程...2020-06-30
- 钩子方法即是在普通的方法上添加"钩子",使特定事件发生时可以被调用,下面就来以实例讲解Ruby中的钩子方法及对方法调用添加钩子...2020-06-30
- 单件方法顾名思义,就是只作用于单个对象的方法,同理单件类就是单件方法所存在的类,规定其作用域,这里我们就来详解Ruby中的单件方法和单件类:...2020-06-30
- gem是一种文件组织的包,一般的ruby的很多插件都有由这种各种的包提供。我们来看看gem的用法...2020-06-30
Ubuntu上配置Ruby on Rails框架及RubyMine IDE开发环境
Ruby on Rails是Ruby世界中当仁不让的Web框架代表,甚至可以说Rails推动了Ruby的流行,这里我们就来看一下如何在Ubuntu上配置Ruby on Rails框架及RubyMine IDE开发环境...2020-06-30Ruby 中的 module_function 和 extend self异同
本文主要给大家介绍了在Ruby中 module_function 和 extend self的共同点和区别,非常的详细,也很实用,方便大家更好的理解的module_function 和 extend self...2020-06-30Ruby on rails安装后去掉DL is deprecated,please use Fiddle警告信息的方法【测试可用】
这篇文章主要介绍了Ruby on rails安装后去掉DL is deprecated,please use Fiddle警告信息的方法,通过针对Ruby on rails安装文件中的警告部分源码进行注释来达到消除警告的目的,需要的朋友可以参考下...2020-06-30- 这篇文章主要介绍了ruby ftp封装实例详解的相关资料,需要的朋友可以参考下...2020-06-30
- 插入排序即是把已有的有序序列从后向前扫描插入元素,数值大的向后移动,这里我们就来看一下使用Ruby实现插入排序算法及进阶的二路插入排序代码示例...2020-06-30
- Hash类型在Ruby中就相当于Python的字典,是一个键值对应的集合类型,这里就来整理一下Ruby中的Hash哈希类型基本操作方法小结:...2020-06-30
Ruby rails 页面跳转(render和redirect_to)
今天在做R.R.log的时候发现个问题,在修改密码的时候如果没有通过校验,没有显示校验错误的信息。...2020-06-30- 在本篇文章里小编给大家分享了关于Ruby创建数组方法的知识点内容,对戏有兴趣的朋友们学习下。...2020-06-25
- 这篇文章主要介绍了C#实现Ruby的负数索引器的相关代码和使用方法,非常简单实用,需要的朋友可以参考下...2020-06-25
win10下使用virtualbox + vagrant配置ruby开发机环境
Vagrant是一个基于Ruby的工具,用于创建和部署虚拟化开发环境。它 使用Oracle的开源VirtualBox虚拟化系统,使用 Chef创建自动化虚拟环境。搭配Cmder如丝般顺滑,实现你所有关于Mac&Linux的幻想...2020-06-30- Rails项目通过Ruby世界中的gem和rake工具来构建起来真的相当方便,这里就给大家整理了一份Ruby on Rails网站项目构建简单指南,需要的朋友可以参考下...2020-06-30
学习Ruby你需要了解的相关知识(rvm, gem, bundle, rake, rails等)
这篇文章主要介绍了学习Ruby你需要了解的相关知识(rvm, gem, bundle, rake, rails等),需要的朋友可以参考下...2020-06-30- 最近在学习lambda演算的相关内容,由于资料不全,学习的过程很是痛苦,下面这篇文章主要给大家介绍了关于如何利用Ruby简单模拟Lambda演算的相关资料,需要的朋友可以参考借鉴,下面来随着小编一起学习学习吧。...2020-06-30