ruby元编程之创建自己的动态方法_ruby专题

method_missing是Ruby元编程(metaprogramming)常用的手法。基本思想是通过实现调用不存在的方法,以便进行回调。典型的例子是:ActiveRecord的动态查找(dynamic finder)。例如:我们有email属性那么就可以调用User.find_by_email('joe@example.com'),虽然, ActiveRecord::Base并没有一个叫做find_by_email的方法。

respond_to? 并不如method_missing出名,常用在当需要确认一个回馈对象需要确认,以便不会因为没有反馈对象,而导致后面的调用出现错误。

下面是一个应用这两者的例子:

示例

我们有类Legislator class,现在,想要给它加一个find_by_first_name('John')的动态调用。实现find(:first_name => 'John')的功能。

复制代码 代码如下:

class Legislator
  #假设这是一个真实的实现
  def find(conditions = {})
  end
 
  #在本身定义毕竟这是他的方法
  def self.method_missing(method_sym, *arguments, &block)
    # the first argument is a Symbol, so you need to_s it if you want to pattern match
    if method_sym.to_s =~ /^find_by_(.*)$/
      find($1.to_sym => arguments.first)
    else
      super
    end
  end
end

那么这个时候调用

复制代码 代码如下:

Legislator.respond_to?(:find_by_first_name) 

将会提示错误,那么继续

复制代码 代码如下:

class Legislator
  # 省略
 
  # It's important to know Object defines respond_to to take two parameters: the method to check, and whether to include private methods
  # http://www.ruby-doc.org/core/classes/Object.html#M000333
  def self.respond_to?(method_sym, include_private = false)
    if method_sym.to_s =~ /^find_by_(.*)$/
      true
    else
      super
    end
  end
end

正如代码注释所述respond_to?需要两个参数,如果,你没有提供将会产生ArgumentError。

相关反射 DRY

如果我们注意到了这里有重复的代码。我们可以参考ActiveRecord的实现封装在ActiveRecord::DynamicFinderMatch,以便避免在method_missing和respond_to?中重复。

复制代码 代码如下:

class LegislatorDynamicFinderMatch
  attr_accessor :attribute
  def initialize(method_sym)
    if method_sym.to_s =~ /^find_by_(.*)$/
      @attribute = $1.to_sym
    end
  end
 
  def match?
    @attribute != nil
  end
end

class Legislator
  def self.method_missing(method_sym, *arguments, &block)
    match = LegislatorDynamicFinderMatch.new(method_sym)
    if match.match?
      find(match.attribute => arguments.first)
    else
      super
    end
  end

  def self.respond_to?(method_sym, include_private = false)
    if LegislatorDynamicFinderMatch.new(method_sym).match?
      true
    else
      super
    end
  end
end

缓存 method_missing

重复多次的method_missing可以考虑缓存。

另外一个我们可以向ActiveRecord 学习的是,当定义method_missing的时候,发送 now-defined方法。如下:

复制代码 代码如下:

class Legislator   
  def self.method_missing(method_sym, *arguments, &block)
    match = LegislatorDynamicFinderMatch.new(method_sym)
    if match.match?
      define_dynamic_finder(method_sym, match.attribute)
      send(method_sym, arguments.first)
    else
      super
    end
  end
 
  protected
 
  def self.define_dynamic_finder(finder, attribute)
    class_eval <<-RUBY
      def self.#{finder}(#{attribute})        # def self.find_by_first_name(first_name)
        find(:#{attribute} => #{attribute})   #   find(:first_name => first_name)
      end                                     # end
    RUBY
  end
end

测试

测试部分如下:

复制代码 代码如下:

describe LegislatorDynamicFinderMatch do
  describe 'find_by_first_name' do
    before do
      @match = LegislatorDynamicFinderMatch.new(:find_by_first_name)
    end
     
    it 'should have attribute :first_name' do
      @match.attribute.should == :first_name
    end
   
    it 'should be a match' do
      @match.should be_a_match
    end
  end
 
  describe 'zomg' do
    before do
      @match = LegislatorDynamicFinderMatch(:zomg)
    end
   
    it 'should have nil attribute' do
      @match.attribute.should be_nil
    end
   
    it 'should not be a match' do
      @match.should_not be_a_match
    end
  end
end

下面是 RSpec 例子:

复制代码 代码如下:

describe Legislator, 'dynamic find_by_first_name' do 
  it 'should call find(:first_name => first_name)' do 
    Legislator.should_receive(:find).with(:first_name => 'John') 
     
    Legislator.find_by_first_name('John') 
  end 
end

时间: 2024-09-24 11:38:19

ruby元编程之创建自己的动态方法_ruby专题的相关文章

ruby元编程之创建自己的动态方法

  这篇文章主要介绍了ruby元编程之创建自己的动态方法,本文讲解使用method_missing和respond_to?创建自己的动态方法,需要的朋友可以参考下 method_missing是Ruby元编程(metaprogramming)常用的手法.基本思想是通过实现调用不存在的方法,以便进行回调.典型的例子是:ActiveRecord的动态查找(dynamic finder).例如:我们有email属性那么就可以调用User.find_by_email('joe@example.com')

实例解析Ruby设计模式编程中Strategy策略模式的使用_ruby专题

今天你的leader兴致冲冲地找到你,希望你可以帮他一个小忙,他现在急着要去开会.要帮什么忙呢?你很好奇. 他对你说,当前你们项目的数据库中有一张用户信息表,里面存放了很用户的数据,现在需要完成一个选择性查询用户信息的功能.他说会传递给你一个包含许多用户名的数组,你需要根据这些用户名把他们相应的数据都给查出来. 这个功能很简单的嘛,你爽快地答应了.由于你们项目使用的是MySQL数据库,你很快地写出了如下代码: require 'mysql' class QueryUtil def find_us

ruby元编程之method_missing的一个使用细节_ruby专题

我们知道顶级域,定义域的self是啥? 复制代码 代码如下: puts self    #main puts self.class #Object 我们知道当一个方法被调用的时候,如果没有对象接受,默认就是self,如: 复制代码 代码如下: def tell_me_who     puts self end tell_me_who  #main  方法调用是这样的步骤,先查找当前对象的所在类的实例方法存在方法与否,如果存在,调用方法,如果不存在则查看superclass,直到 BasicObj

ruby安装gem包失败的通用解决方法_ruby专题

ruby语言升级还是比较勤快的.但是数量众多的版本使得程序库的兼容性成了大问题.有些gem表示明确不支持某个特定版本以前的ruby,而有些gem则与较高的版本不兼容.再加上gem本身也有版本,简直是乱成了一锅粥.即使使用了rvm.rbenv之类ruby版本管理工具也避免不了掉入坑中.并且时不时的一些其它环境设置也给你捣乱.所以一般使用ruby程序时,对升级ruby版本或各种gem版本都是比较慎重的,避免一时手贱掉入坑中. 当然你也不能因此就做缩头乌龟,某些情况下还是不得不升级的.比如想使用rub

Ruby中嵌套对象转换成json的方法_ruby专题

JSON由于其数据结构简单便利,已逐渐成为了互联网上的主流数据交换的数据格式. 在讨论嵌套对象(Nested Object)的JSON转换方法之前,我们先看简单的ruby JSON转换.首先,ruby对象转换为JSON字符串: 复制代码 代码如下: class Obj1     def initialize(var1)         @var1 = var1     end     def to_json(*a)         {             "json_class"

在Ruby on Rails上使用Redis Store的方法_ruby专题

Redis Store 是一个专为Ruby应用程序服务的工具包,原生就支持分片,主从复制,编组以及超时和命名空间.此外,在Ruby on Rails上使用它也是非常的简单. 如何使用:对于在Rails上使用Redis Store,首先我们需要在Gemfile文件中添加入口   gem 'redis-rails' gem 'redis-rack-cache' # optional 然后我们就会有如下选择: ## Cache Store # config/environments/productio

Ruby中相等性判断的4种方法_ruby专题

很早就知道 ruby 有 4 种相等性判断方法,分别是:"==","===","equal?" 和 "eql?",平常程序中都有使用,但是感觉对其缺乏深入理解,今天读 rails 部分源码的时候拿捏不定其中一个判断的意思,于是趁机深入研究了一番,总算觉得比较清楚了,今天做一下笔记,以作备忘. "==" 最常见的相等性判断 "==" 使用最频繁,它通常用于对象的值相等性(语义相等)判断,在

Ruby元编程的白魔法书

在Ruby的世界中,程序员们享受着各种光怪陆离的语法糖,也经历着各种各样的陷阱.而这一切的根本就在于Ruby强大 的元编程能力.元编程就像Ruby世界的魔法,当其是白魔法的时候可以帮助你把程序变得异常简洁,美观:而当其是黑魔法 的时候,你将会迷失在一些很难解释的Bug中. <Ruby元编程>就是一部告诉大家如何使用,控制Ruby元编程魔法的 秘籍.该书的写作手法非常值得称道,作者把所有的知识点浓缩在了一个星期的工作过程中,通过一个菜鸟和大牛针对项目 中遇到的各种问题的讨论,解决来引入各种元编程

Ruby元编程之梦中情人method

  这篇文章主要介绍了Ruby元编程之梦中情人method_missing方法详解,本文讲解了我该怎么用 method_missing .方法代理.define_method.什么时候用 method_missing.元方法等内容,需要的朋友可以参考下 我最近读了些文章(比如这篇),宣传在 Ruby 里使用 method_missing 的. 很多人都与 method_missing 干柴烈火,但在并没有小心处理彼此之间的关系.所以,我想来探讨一下这个问题: ** 我该怎么用 method_mi