亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Ruby > 正文

Ruby元編程技術(shù)詳解(Ruby Metaprogramming techniques)

2024-07-21 23:04:43
字體:
供稿:網(wǎng)友

我最近考慮了很多元編程(Metaprogramming)的問題,并希望看到更多這方面技術(shù)的例子和講解。無論好壞,元編程已經(jīng)進(jìn)入Ruby社區(qū),并成為完成各種任務(wù)和簡化代碼的標(biāo)準(zhǔn)方式。既然找不到這類資源,我準(zhǔn)備拋磚引玉寫一些通用Ruby技術(shù)的文章。這些內(nèi)容可能對從其它語言轉(zhuǎn)向Ruby或者還沒有體驗到Ruby元編程樂趣的程序員非常有用。

1. 使用單例類 Use the singleton-class

  許多操作單個對象的方法是基于操作其單例類(singleton class),并且這樣可以使元編程更簡單。獲得單例類的經(jīng)典方法是執(zhí)行如下代碼:

復(fù)制代碼 代碼如下:

sclass = (class << self; self; end) 

  RCR231建議這樣定義Kernel#singleton_class方法:
復(fù)制代碼 代碼如下:

module Kernel  
  def singleton_class  
    class << self; self; end 
  end 
end 

  我會在下文使用這個方法。

 

2. DSL的使用類方法來修改子類 Write DSL's using class-methods that rewrite subclasses

  當(dāng)你想創(chuàng)建一個DSL來定義類信息時,最常見的問題是怎樣表示信息來讓框架的其它部分使用。以定義一個ActiveRecord模型對象為例:

復(fù)制代碼 代碼如下:

class Product < ActiveRecord::Base  
  set_table_name 'produce'   
end 

 

  在這個例子中,令人感興趣的是set_table_name的使用。這是怎么起作用的呢?好吧,這里涉及到一個小魔法。這是一種實(shí)現(xiàn)方法:

復(fù)制代碼 代碼如下:

module ActiveRecord  
  class Base  
    def self.set_table_name name  
      define_attr_method :table_name, name  
    end 
    def self.define_attr_method(name, value)  
      singleton_class.send :alias_method, "original_#{name}", name  
      singleton_class.class_eval do   
        define_method(name) do     
          value  
        end 
      end 
    end 
  end   
end 

 

  這里令人感興趣的是define_attr_method。在這個例子中我們需要獲得Product類的單例類,但又不想修改ActiveRecord::Base。通過使用單例類我們達(dá)到了這個目的。我們?yōu)樵瓉淼姆椒ㄈe名,再定義新的存取器(accessor)來返回值。如果ActiveRecord需要table name就可以直接調(diào)用存取器。這種動態(tài)創(chuàng)建方法和存取器的技術(shù)在單例類是很常見的,特別是Rails。

3. 動態(tài)創(chuàng)建class和module Create classes and modules dynamically

  Ruby允許你動態(tài)創(chuàng)建和修改class和module。你可以在沒有凍結(jié)的class或module上做任何修改。特定情況下會很有用。Struct類可能是最好的例子:

復(fù)制代碼 代碼如下:

PersonVO = Struct.new(:name, :phone, :email)  
p1 = PersonVO.new(:name => "Ola Bini") 

  這會創(chuàng)建一個新類,并賦給PersonVO,然后創(chuàng)建一個類的實(shí)例。從草稿創(chuàng)建新類并定義新方法也很簡單:
復(fù)制代碼 代碼如下:

c = Class.new 
c.class_eval do 
  define_method :foo do 
    puts "Hello World" 
  end 
end 
c.new.foo    # => "Hello World" 

  除了Struct,還能在SOAP4R和Camping找到輕松創(chuàng)建類的例子。Camping尤其令人感興趣,因為它有專門的方法創(chuàng)建這些類,被你的controller和view繼承。Camping的許多有趣的功能都是用這種方式實(shí)現(xiàn)的:
復(fù)制代碼 代碼如下:

def R(*urls); Class.new(R) { meta_def(:urls) { urls } };   
end 

 

  這使得可以這樣創(chuàng)建controller:
class View < R '/view/(/d+)' 
  def get post_id  
  end 
end 


  你也可以這樣創(chuàng)建module,然后在類中包含module。

 

4. 使用method_missing來做有趣的事 Use method_missing to do interesting things

  除了閉包(block),method_missing可能是Ruby最強(qiáng)大的特性,也是最容易濫用的一個。用好method_missing的話有些代碼會變得超級簡單,甚至是不能缺少。一個好的例子(Camping)是擴(kuò)展Hash:

復(fù)制代碼 代碼如下:

class Hash 
  def method_missing(m,*a)  
    if m.to_s =~ /=$/  
      self[$`] = a[0]  
    elsif a.empty?    
      self[m]  
    else 
      raise NoMethodError, "#{m}" 
    end 
  end 
end 

  就可以這樣使用hash:
復(fù)制代碼 代碼如下:

x = {'abc' => 123}  
x.abc # => 123  
x.foo = :baz 
x # => {'abc' => 123, 'foo' => :baz} 

  如你所見,如果有人調(diào)用了一個hash不存在的方法,則會搜索內(nèi)部集合。如果方法名以=結(jié)尾,則會賦給同名的key。

 

  Markaby中可以找到另一個很好的method_missing技巧。以下引用的代碼可以生成任何包含CSS class的XHTML標(biāo)簽:

復(fù)制代碼 代碼如下:

body do 
  h1.header 'Blog' 
  div.content do 
    'Hellu' 
  end 
end 

會生成:
復(fù)制代碼 代碼如下:

<body> 
  <h1 class="header">Blog</h1> 
  <div class="content"> 
    Hellu  
  </div> 
</body> 

  絕大多數(shù)這種功能,特別是CSS class名是通過method_missing設(shè)置了self的屬性然后返回self。

 

5. 方法模式的調(diào)度 Dispatch on method-patterns

  這對于無法預(yù)測的方法來說可以輕松的達(dá)到可擴(kuò)展性。我最近創(chuàng)建了一個小型驗證框架,核心的驗證類會找出自身所有以check_開頭的方法并調(diào)用,這樣就可以輕松地增加新的驗證:只要往類或?qū)嵗刑砑有路椒ā?br /> methods.grep /^check_/ do |m|  
  self.send m  
end 

  這非常簡單,并且難以置信的強(qiáng)大??梢钥匆幌耇est::Unit到處使用這種方法。

6. 替換方法 Replacing methods

  有時候一個方法的實(shí)現(xiàn)不是你要的,或者只做了一半。標(biāo)準(zhǔn)的面向?qū)ο蠓椒ㄊ抢^承并重載,再調(diào)用父類方法。僅當(dāng)你有對象實(shí)例化的控制權(quán)時才有用,經(jīng)常不是這種情況,繼承也就沒有價值。為得到同樣的功能,可以重命名(alias)舊方法,并添加一個新的方法定義來調(diào)用舊方法,并確保舊方法的前后條件得到保留。

復(fù)制代碼 代碼如下:

class String 
  alias_method :original_reverse, :reverse 
  def reverse   
    puts "reversing, please wait..." original_reverse  
  end 
end 

  一個極端的用法是臨時修改一個方法,然后再還原。例如:
復(fù)制代碼 代碼如下:

def trace(*mths)  
  add_tracing(*mths) # aliases the methods named, adding tracing      
  yield 
  remove_tracing(*mths) # removes the tracing aliases  
end 

  這個例子展示了編寫add_tracing和remove_tracing的一種典型方法。它依賴于第1條的單例類:
復(fù)制代碼 代碼如下:

class Object    
  def add_tracing(*mths)      
    mths.each do |m|   
      singleton_class.send :alias_method, "traced_#{m}", m   
      singleton_class.send :define_method, m do |*args|  
        $stderr.puts "before #{m}(#{args.inspect})" 
        ret = self.send("traced_#{m}", *args)  
        $stderr.puts "after #{m} - #{ret.inspect}" 
        ret  
      end 
    end    
  end 
  def remove_tracing(*mths)     
    mths.each do |m|  
      singleton_class.send :alias_method, m, "traced_#{m}" 
    end 
  end 
end 
"abc".add_tracing :reverse 

  如果這些方法是添加到module(有一點(diǎn)點(diǎn)不同,看你能不能寫出來?。阋部梢栽陬惗菍?shí)例上添加和刪除tracing。

 

7. 使用nil類來引入空對象的重構(gòu) Use NilClass to implement the Introduce Null Object refactoring

  在Fowler的重構(gòu)中,“引入空對象”的重構(gòu)是一個對象要么存在,要么為空時有一個預(yù)定義值。典型例子如下:

復(fù)制代碼 代碼如下:

name = x.nil? ? "default name" : x.name 

  目前基于Java的重構(gòu)會推薦創(chuàng)建一個類似于null的子類。例如NullPerson會繼承Person,重載name方法總是返回"default name"。但是在Ruby中我們可以打開類,可以這樣做:
復(fù)制代碼 代碼如下:

def nil.name; "default name"; end 
x # => nil  
name = x.name # => "default name" 

 

8. 學(xué)習(xí)eval的不同版本 Learn the different versions of eval

  Ruby有幾種版本的執(zhí)行方法(evaluation)。了解它們的區(qū)別和使用情景是很重要的。有eval、instance_eval、module_eval和class_eval幾種。首先,class_eval是module_eval的別名。其次,eval和其他的有些不同。最重要的是eval只能夠執(zhí)行一個字符串,其它的可以執(zhí)行block。這意味著eval是你做任何事的最后選擇,它有它的用處,但絕大多數(shù)情況下應(yīng)該用instance_eval和module_eval執(zhí)行block。

  eval會在當(dāng)前環(huán)境執(zhí)行字符串,除非環(huán)境已經(jīng)提供綁定(binding)。(見第11條)

  instance_eval會在接收者(reveiver)的上下文中執(zhí)行字符串或block,沒有指定的話self會作為接收者。

  module_eval會在調(diào)用的module的上下文中執(zhí)行字符串或block。這個比較適合在module或單例類中定義新方法。instance_eval和module_eval的主要區(qū)別在于定義的方法會放在哪里。如果你用String.instance_eval定義foo方法會得到String.foo,如果是用module_eval會得到String.new.foo。

  module_eval幾乎總是適用;要像對待瘟疫一樣避免使用eval。遵守這些簡單的規(guī)則會對你有好處。


9. 實(shí)例變量的內(nèi)省 Introspect on instance variables

  Rails使用了一個技巧來使controller中的實(shí)例變量也能用在view中,就是內(nèi)省一個對象的實(shí)例變量。這會嚴(yán)重破壞封裝,然而有時候確實(shí)非常順手。可以很容易的通過instance_variables、instance_variable_get和instance_variable_set實(shí)現(xiàn)。要把所有實(shí)例變量從一個復(fù)制到另一個,可以這樣:

復(fù)制代碼 代碼如下:

from.instance_variables.each do |v|  
  to.instance_variable_set v, from.instance_variable_get(v)  
end 

 

10. 從block創(chuàng)建Proc并公開 Create Procs from blocks and send them around

  把一個Proc實(shí)例化保存在變量中并公開的做法使得很多API容易使用。這是Markaby用來管理CSS class定義的一種方法。很容易把block轉(zhuǎn)換成Proc:
def create_proc(&p); p; end 
create_proc do 
  puts "hello" 
end       # => #<Proc ...> 

  調(diào)用也很容易:
p.call(*args) 

  如果要用proc來定義方法,應(yīng)該用lambda來創(chuàng)建,就可以用return和break:
p = lambda { puts "hoho"; return 1 }  
define_method(:a, &p) 

  如果有block的話method_missing會調(diào)用block:
def method_missing(name, *args, &block)  
  block.call(*args) if block_given?  
end 
thismethoddoesntexist("abc","cde") do |*args|  
  p args  
end  # => ["abc","cde"] 


11. 用綁定(binding)來控制eval Use binding to control your evaluations

  如果你確實(shí)需要用eval,你可以控制哪些變量是有效的。這時候要用kernel方法binding來獲得所綁定的對象。例如:

復(fù)制代碼 代碼如下:

def get_b; binding; end 
foo = 13  
eval("puts foo",get_b) # => NameError: undefined local variable or method `foo' for main:Object 

  ERb和Rails用這種技術(shù)來設(shè)置哪些實(shí)例變量是有效的。例如:
復(fù)制代碼 代碼如下:

class Holder  
  def get_b; binding; end 
end 
h = Holder.new 
h.instance_variable_set "@foo", 25  
eval("@foo",h.get_b) 

 

  希望這些技巧和技術(shù)已經(jīng)為您闡明了元編程。我并不聲稱自己是Ruby或者元編程方面的專家,這只是我對這個問題的一些想法。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
色综合天天综合网国产成人网| 久久精品亚洲一区二区三区浴池| 综合久久给合久久狠狠狠97色| 亚洲熟妇无码一区二区三区导航| 隔壁老王国产在线精品| 午夜视频免费播放| 韩国美女主播一区| 欧洲激情综合| 成人动漫视频在线观看| av有声小说一区二区三区| 精品欧美国产一区二区三区| 亚洲黄色免费观看| 国产美女在线免费观看| 小早川怜子一区二区的演员表| 亚洲va在线va天堂成人| 精品视频二区| 欧洲永久精品大片ww免费漫画| 国产一级久久久久毛片精品| 国产一二三四| 亚洲人成无码www久久久| 深夜福利国产精品| 亚洲日本精品视频| 欧美va天堂| 国产精品无码一区二区在线| 一区二区三区日韩| 欧美成人免费全部网站| 国产乱码午夜在线视频| 亚洲av无码一区二区三区在线| av电影在线观看网址| 欧美wwwxxxx| 国产亚洲一区二区三区不卡| 91麻豆精品| 日韩欧美综合视频| 国产福利免费在线观看| 婷婷精品国产一区二区三区日韩| 成人免费视频一区| av中文字幕在线观看第一页| 香蕉大人久久国产成人av| 久久亚洲综合网| 亚洲第一导航| 精品国产无码AV| 久久国产精品久久久久| 国产精品女主播视频| 九九九在线视频| 国产精品视频久久久久久久| 欧美色videos| 五月天电影免费在线观看一区| 欧美第十八页| 97久久超碰福利国产精品…| 91theporn国产在线观看| 永久av免费网站| 在线小视频网址| 在线视频欧美精品| 亚洲色在线视频| 91九色视频蝌蚪| 国产精品成人免费一区二区视频| 国产精品久久久久一区二区三区厕所| av资源中文色综合| 亚洲熟女www一区二区三区| 日韩一级在线视频| 日韩欧美aaa| 26uuu国产| 亚洲黄色大片| 生活片a∨在线观看| av福利在线观看| 九七电影院97理论片久久tvb| 国产黄色免费在线观看| 凹凸日日摸日日碰夜夜爽1| 折磨小男生性器羞耻的故事| 久久久久亚洲精品国产| 人体精品一二三区| 综合久久av| 午夜视频免费在线| 国产无遮挡猛进猛出免费软件| 免费国产黄线在线观看视频| 免费成人结看片| 久久精品一区二区三区不卡牛牛| 四虎永久在线精品免费一区二区| 亚洲精品国产精品国自产观看| 久久一夜天堂av一区二区三区| 精品中文字幕在线2019| 91精品国自产在线观看| 黄色一级视频免费观看| 日本五十路女优| 国产精品theporn88| 日本少妇裸体做爰| 天天干天天干天天| 2022中文字幕| 久久久国产精品无码| 欧美综合第一页| a级大胆欧美人体大胆666| 视频区小说区图片区| 欧美一区视频在线| 亚洲精品国产熟女久久久| 免费国产羞羞网站美图| 成人av蜜桃| 久久九九电影| 欧美精品videosex极品1| 成年女人a毛片免费视频| 日韩精品综合在线| 美女精品在线| 欧美激情乱人伦一区| 国产亚洲观看| 久久亚洲综合国产精品99麻豆精品福利| 黄页网址大全在线观看| 亚洲综合激情五月| 1024日韩| a在线观看视频| 色婷婷综合成人av| 国产精品丝袜久久久久久app| 日韩免费一区二区三区| 精品国产av一区二区三区| 亚洲专区免费| 成人资源av| 男女啊啊啊视频| 国产日韩欧美精品一区二区三区| 亚洲精品在线二区| 中文字幕电影在线| 九色在线免费| 欧美凹凸一区二区三区视频| 中文字幕xxxx| 制服丝袜av成人在线看| 久久99久久99小草精品免视看| 国语对白永久免费| 91精品啪在线观看国产18| 亚洲在线观看网站| 亚洲免费视频成人| 麻豆免费在线观看视频| 欧美xxx另类| 一级少妇精品久久久久久久| 尤物国产精品| cao在线视频| 97人妻一区二区精品视频| 亚洲欧洲美洲综合色网| xxx国产精品| 日本黄色三级视频| 亚洲久久中文字幕| 人妻少妇精品久久| 国产一区二区三区四区五区3d| 国产精品成av人在线视午夜片| 三级毛片网站| 久久精品国产视频| 欧美日韩电影一区二区三区| 亚洲三级国产| 香蕉成人在线视频| 欧美一级视频在线| 在线视频国产一区| 99久久婷婷国产综合精品首页| 在线观看国产一区二区| 国产精品黄色网| 精品福利一区| 91久久夜色精品国产网站| 亚洲调教视频在线观看| 国产亚洲欧洲| 精品国产91| 女人香蕉久久**毛片精品| 日本中文字幕伦在线观看| 亚洲电影一级片| 久久国产精品亚洲人一区二区三区| 天天干中文字幕| 亚洲AV无码成人精品一区| 久草视频免费在线| 日本韩国欧美| 操碰免费视频| 性欧美极品xxxx欧美一区二区| av天堂一区二区三区| 国产99久久九九精品无码| 国产欧美日本一区视频| 久草在线资源网| av高清在线免费观看| 嫩草伊人久久精品少妇av杨幂| 欧美日本在线视频中文字字幕| 久久久av亚洲男天堂| 91蜜桃在线视频| 深夜福利一区二区| 日韩综合一区二区三区| 一个人看的日本www的免费视频| 午夜欧美一区二区三区在线播放| 石原莉奈一区二区三区高清在线| 久久久91精品国产一区二区三区| 久久草av在线| 欧美日韩在线电影| 天天色天天操天天射| 国产精品视频二区三区| 免费成人在线观看av| 亚洲乱码av中文一区二区| 成人午夜视频福利| 五月婷婷丁香综合网| 免费成人高清视频| 午夜精品影院| 尤物精品在线| 黄色三级中文字幕| 久久综合一区二区三区| 伪装者免费全集在线观看| 精品国产伦一区二区三区| 国产日本在线视频| 亚洲一区精品在线| 中文在线免费观看| 男女午夜激烈无遮挡| 精品日韩视频| 91在线观看下载| 成人久久久久| 久久精品色欧美aⅴ一区二区| 国产精品久久久久一区二区国产| 国产一级淫片a| 国产精品对白交换视频| 国产一级视频在线播放| 欧美黑人国产人伦爽爽爽| 欧美日韩一级大片网址| 97在线资源在| 男人天堂2024| √最新版天堂资源网在线| 国产精品一二三产区| 欧美狂猛xxxxx乱大交3| 欧美激情福利| 日韩一级黄色大片| 国产精品亚洲第五区在线| 欧美成人精品在线| 五月天中文字幕一区二区| 成视频年人免费看黄网站| 国产精品日本一区二区不卡视频| 玖玖在线精品| 国产三级理论片| 国产日产精品久久久久久婷婷| 欧美久久久久久久久久| 国产亚洲一本大道中文在线| 亚洲国产综合一区| 狠狠躁夜夜躁人人爽天天天天97| 性色国产成人久久久精品| 亚洲九九爱视频| 国产成人免费av在线| 国产中文字幕在线视频| 成人免费网站在线| 蜜桃视频久久一区免费观看入口| 欧美激情国内偷拍| 国产日韩欧美中文| 亚洲成人在线| 精品久久免费视频| 欧美激情理论| 日韩一级淫片| 中文字幕中文字幕在线中文字幕三区| 四虎成人在线播放| 成人免费黄色网址| 精品一区二区三区人妻| 亚洲AV无码国产精品午夜字幕| 亚洲色图视频免费播放| 久久久国产精品不卡| 成人久久综合| 在线视频精品一| 性欧美xxx69hd高清| 日韩成人精品在线观看| 青草热久免费精品视频| 高清电影在线免费观看| 91蜜桃视频在线| 久久成人国产精品| 午夜精品久久久久久久99热影院| 亚洲激情婷婷| 欧美日韩在线大尺度| 11024精品一区二区三区日韩| 日本少妇全体裸体洗澡| 四虎永久免费地址| 伊人伊人av电影| 一区二区在线免费视频| 免费国产在线视频| 亚洲午夜激情网站| 国产日韩中文字幕在线| 国产精品久久久久99| 亚洲va在线va天堂| 99久久婷婷国产综合精品| 日韩av电影手机在线| 一区在线中文字幕| 一区二区欧美亚洲| 一级特黄大欧美久久久| av大片免费在线观看| 色一情一乱一乱一区91| aaaaaaa大片免费看| 日本欧美大码aⅴ在线播放| 污视频在线免费| 视频午夜在线| 欧美精产国品一二三区| 日本一区二区在线视频| 91女厕偷拍女厕偷拍高清| 91免费国产视频网站| 欧美成人精品在线播放| 一本久道久久综合婷婷鲸鱼| 欧美成人午夜电影| 中文字幕国产高清| 亚洲国产精品久久久天堂| 99国产在线播放| 欧美aaa免费| 欧美精品一区二区久久婷婷| 久久成人免费网站| 国产日韩欧美一区| 国产又大又粗又长| 国产三级自拍| 国产第一页浮力| 国产欧美综合在线| 好吊妞这里只有精品| 亚洲成人动漫在线播放| 97在线观看| 中文字幕亚洲精品视频| 国产精品久久久免费看| 一本之道在线视频| 久久亚洲国产成人亚| 国产精品suv一区二区88| 天天色天天干天天| 粉嫩av一区二区三区| 亚洲第一天堂av| 性一交一乱一伧老太| www.国产福利| 91色porny在线视频| 蜜桃视频无码区在线观看| 午夜精品视频一区| 本田岬高潮一区二区三区| 亚洲欧美日韩一二三区| 亚洲欧美日本伦理| av日韩中文| 亚洲精品午夜| 日本在线播放不卡| 女人在下体塞跳蛋在线观看| 国产99久久九九精品无码免费| 国产精品国产自产拍在线| 自拍偷拍第1页| 日韩av毛片| 久久青青草原一区二区| 午夜免费福利影院| 日干夜干天天干| 国产美女亚洲精品7777|