最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2015年04月29日 09:51:17   投稿:junjie  
這篇文章主要介紹了Ruby元編程技術(shù)詳解(Ruby Metaprogramming techniques),本文講解了11個(gè)關(guān)于元編程的技術(shù)和技巧,需要的朋友可以參考下

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

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

  許多操作單個(gè)對(duì)象的方法是基于操作其單例類(singleton class),并且這樣可以使元編程更簡(jiǎn)單。獲得單例類的經(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 

  我會(huì)在下文使用這個(gè)方法。

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

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

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

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

  在這個(gè)例子中,令人感興趣的是set_table_name的使用。這是怎么起作用的呢?好吧,這里涉及到一個(gè)小魔法。這是一種實(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。在這個(gè)例子中我們需要獲得Product類的單例類,但又不想修改ActiveRecord::Base。通過(guò)使用單例類我們達(dá)到了這個(gè)目的。我們?yōu)樵瓉?lái)的方法取別名,再定義新的存取器(accessor)來(lái)返回值。如果ActiveRecord需要table name就可以直接調(diào)用存取器。這種動(dòng)態(tài)創(chuàng)建方法和存取器的技術(shù)在單例類是很常見的,特別是Rails。

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

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

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

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

  這會(huì)創(chuàng)建一個(gè)新類,并賦給PersonVO,然后創(chuàng)建一個(gè)類的實(shí)例。從草稿創(chuàng)建新類并定義新方法也很簡(jiǎn)單:
復(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尤其令人感興趣,因?yàn)樗袑iT的方法創(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來(lái)做有趣的事 Use method_missing to do interesting things

  除了閉包(block),method_missing可能是Ruby最強(qiáng)大的特性,也是最容易濫用的一個(gè)。用好method_missing的話有些代碼會(huì)變得超級(jí)簡(jiǎn)單,甚至是不能缺少。一個(gè)好的例子(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)用了一個(gè)hash不存在的方法,則會(huì)搜索內(nèi)部集合。如果方法名以=結(jié)尾,則會(huì)賦給同名的key。

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

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

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

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

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

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

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

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

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

6. 替換方法 Replacing methods

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

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

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

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

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

  這個(gè)例子展示了編寫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)不同,看你能不能寫出來(lái)?。?,你也可以在類而非實(shí)例上添加和刪除tracing。

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

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

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

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

  目前基于Java的重構(gòu)會(huì)推薦創(chuàng)建一個(gè)類似于null的子類。例如NullPerson會(huì)繼承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í)行一個(gè)字符串,其它的可以執(zhí)行block。這意味著eval是你做任何事的最后選擇,它有它的用處,但絕大多數(shù)情況下應(yīng)該用instance_eval和module_eval執(zhí)行block。

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

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

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

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


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

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

復(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

  把一個(gè)Proc實(shí)例化保存在變量中并公開的做法使得很多API容易使用。這是Markaby用來(lái)管理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來(lái)定義方法,應(yīng)該用lambda來(lái)創(chuàng)建,就可以用return和break:
p = lambda { puts "hoho"; return 1 }  
define_method(:a, &p) 

  如果有block的話method_missing會(huì)調(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)來(lái)控制eval Use binding to control your evaluations

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

復(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ù)來(lái)設(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或者元編程方面的專家,這只是我對(duì)這個(gè)問(wèn)題的一些想法。

相關(guān)文章

  • Lua進(jìn)階教程之閉包函數(shù)、元表實(shí)例介紹

    Lua進(jìn)階教程之閉包函數(shù)、元表實(shí)例介紹

    這篇文章主要介紹了Lua進(jìn)階教程之閉包函數(shù)、元表實(shí)例介紹,本文詳細(xì)講解了Lua的閉包函數(shù)和元表,并同時(shí)和C++做了比較,需要的朋友可以參考下
    2014-09-09
  • Lua math.fmod使用時(shí)的小數(shù)問(wèn)題

    Lua math.fmod使用時(shí)的小數(shù)問(wèn)題

    這篇文章主要介紹了Lua math.fmod使用時(shí)的小數(shù)問(wèn)題,math.fmod用于取模運(yùn)算,使用小數(shù)時(shí)可能會(huì)遇到不可預(yù)料的結(jié)果,所以應(yīng)該避免使用小數(shù),需要的朋友可以參考下
    2015-06-06
  • phpredis執(zhí)行LUA腳本示例代碼

    phpredis執(zhí)行LUA腳本示例代碼

    這篇文章主要給大家介紹了關(guān)于phpredis執(zhí)行LUA腳本的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-10-10
  • Lua簡(jiǎn)介、編譯安裝教程及變量等語(yǔ)法介紹

    Lua簡(jiǎn)介、編譯安裝教程及變量等語(yǔ)法介紹

    這篇文章主要介紹了Lua簡(jiǎn)介、編譯安裝教程及變量等語(yǔ)法介紹,本文同時(shí)講解了lua注釋語(yǔ)法、Lua命令行方式等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • Lua中的函數(shù)相關(guān)知識(shí)點(diǎn)整理匯總

    Lua中的函數(shù)相關(guān)知識(shí)點(diǎn)整理匯總

    這篇文章主要介紹了Lua中的函數(shù)相關(guān)知識(shí)點(diǎn)整理匯總,包括函數(shù)的參數(shù)傳遞和定義函數(shù)等基本知識(shí),需要的朋友可以參考下
    2015-05-05
  • C語(yǔ)言模塊回調(diào)Lua函數(shù)的兩種方法

    C語(yǔ)言模塊回調(diào)Lua函數(shù)的兩種方法

    這篇文章主要介紹了C語(yǔ)言模塊回調(diào)Lua函數(shù)的兩種方法,本文講解了C保存函數(shù)對(duì)象、C訪問(wèn)Lua全局環(huán)境兩種方法,需要的朋友可以參考下
    2015-04-04
  • C語(yǔ)言中調(diào)用Lua函數(shù)實(shí)例

    C語(yǔ)言中調(diào)用Lua函數(shù)實(shí)例

    這篇文章主要介紹了C語(yǔ)言中調(diào)用Lua函數(shù)實(shí)例,本文講解了調(diào)用一個(gè)Lua函數(shù)的步驟和C語(yǔ)言調(diào)用Lua函數(shù)實(shí)例,需要的朋友可以參考下
    2015-04-04
  • Lua腳本獲取喜馬拉雅MP3音頻地址

    Lua腳本獲取喜馬拉雅MP3音頻地址

    這篇文章主要介紹了Lua腳本獲取喜馬拉雅MP3音頻地址,本文直接給出代碼實(shí)例,需要的朋友可以參考下
    2015-04-04
  • 最新評(píng)論

    顺昌县| 徐州市| 旬阳县| 阿拉尔市| 荆门市| 临湘市| 芦溪县| 衡阳县| 尉氏县| 武强县| 麦盖提县| 西充县| 濮阳县| 霍林郭勒市| 独山县| 宜丰县| 通化市| 晴隆县| 九台市| 揭阳市| 金坛市| 益阳市| 壶关县| 开江县| 西贡区| 建宁县| 环江| 中山市| 西和县| 运城市| 龙门县| 平顺县| 盐城市| 济阳县| 筠连县| 桑日县| 卓资县| 鄂伦春自治旗| 宁武县| 西丰县| 瑞安市|