Ruby 元编程

1. 什么是元编程?

元編程(英語:Metaprogramming),又譯超編程,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)做为它们的資料,或者在运行时完成部分本应在编译时完成的工做。多数状况下,与手工编写所有代码相比,程序员能够得到更高的工做效率,或者给与程序更大的灵活度去处理新的情形而无需从新编译。程序员

元编程技术最先应该是由 Smalltalk 开始使用,John M. Vlissides 在 Pattern Languages of Program Design 一书中写到:编程

Lisp社团位于如今称之为“反射编程”(reflective programming)或“元编程”(metaprogramming)——对可编程语言编程的前沿。Smalltalk事实上从20世纪70年代后期就开始支持元类。但它是Lisp专用语言,从Comman和ObjVlisp开始,推进元编程成为主流[Bobrow+86,Cointe87]。早期工做为主对象协议(Metaobject Protocal)[Kiczales+91]打下了基础。主对象协议主要用来推广元编程。商业系统也开始使用元编程,特别是在IBM SOM平台上。api

2. Ruby中的元编程

  • 打开类ruby

     2.5.0 :071 > 'test'.happy
     Traceback (most recent call last):
             2: from /Users/zh/.rvm/rubies/ruby-2.5.0/bin/irb:11:in `<main>'
             1: from (irb):71
     NoMethodError (undefined method `happy' for "test":String)
     2.5.0 :072 > class String
     2.5.0 :073?>   def happy
     2.5.0 :074?>     'Good time'
     2.5.0 :075?>   end
     2.5.0 :076?> end
      => :happy
     2.5.0 :077 > 'test'.happy
      => "Good time"
     2.5.0 :096 > String.remove_method(:happy)
      => String
     2.5.0 :097 > 'test'.happy
     Traceback (most recent call last):
             2: from /Users/zh/.rvm/rubies/ruby-2.5.0/bin/irb:11:in `<main>'
             1: from (irb):97
     NoMethodError (undefined method `happy' for "今天不上班":String)
    复制代码
     2.5.0 :089 > class Integer
     2.5.0 :090?>   def add(number)
     2.5.0 :091?>     self + number
     2.5.0 :092?>   end
     2.5.0 :093?> end
      => :add
     2.5.0 :094 > 1.add(2) # 1 + 2
      => 3
    复制代码
  • define_methodmarkdown

     2.5.0 :101?>   %w(a b c d e f).each do |method_name|
     2.5.0 :102 >       define_method("test_#{method_name}"){ p method_name }
     2.5.0 :103?>     end
     2.5.0 :104?>   end
      => ["a", "b", "c", "d", "e", "f"]
     2.5.0 :105 > 'test'.test_a
     "a"
      => "a"
     2.5.0 :106 > 'test'.test_f
     "f"
      => "f"
    复制代码
  • send #public_sendapp

     currency = Currency.find('btc') 
     ['hot', 'cold'].each do |wallet_type| 
       currency.send("#{wallet_type}_wallets")
     end
     ​
     def send_email
        params = {
            address: member.email,
            amount: amount,
            currency: currency.display_name,
            time_stamp: Time.now.to_s,
            subject: "Withdraw #{aasm_state.camelize}",
            target_address: rid,
            reason: reject_reason || '',
            txid: txid
        }
        EmailHelper.send("send_withdraw_#{aasm_state}", params)
      rescue StandardError => e
        report_exception(e)
      end
    复制代码
  • Procless

     # 方法1
     inc = Proc.new { | x | x + 1}
     inc.call(2)  #=> 3
     ​
     # 方法2
     inc = proc {|x| x + 1 }
     inc.call(2) #=> 3
     ​
     # 方法3
     inc = ->(x=2) { x + 1}
     inc.call(2) #=> 3
     ​
     # 方法4
     inc = lambda {| x | x + 1 }
     inc.call(2)  #=> 3
    复制代码
     2.5.0 :189 > def my_method(&the_proc)
     2.5.0 :190?>   the_proc
               return 1
     2.5.0 :191?> end
      => :my_method
     2.5.0 :197 > my_method{ 'I love ruby' }
      => #<Proc:0x00007fae428636c8@(irb):197>
     2.5.0 :198 >  my_method{ |x| 'I love ruby' + x }.call(1)
      => "I love ruby"
    复制代码
  • alias编程语言

     2.5.0 :221 > class MyClass
     2.5.0 :222?>   def my_method
     2.5.0 :223?>     'my_method'
     2.5.0 :224?>     end
     2.5.0 :225?>   alias :m :my_method
     2.5.0 :226?> end
      => nil
     2.5.0 :227 > MyClass.new.m
      => "my_method"
     2.5.0 :228 > MyClass.new.my_method
      => "my_method"
    复制代码
  • Instance_evalide

     2.5.0 :183 > class MyClass
     2.5.0 :184?>   def initialize
     2.5.0 :185?>     @v = 1
     2.5.0 :186?>   end
     2.5.0 :187?> end
     2.5.0 :187 > MyClass.new.instance_eval('@v')
      => 1
    复制代码
  • Bindingoop

     2.5.0 :261 > class MyClass
     2.5.0 :262?>   def my_method
     2.5.0 :263?>      v = 1
     2.5.0 :264?>      binding
     2.5.0 :265?>   end
     2.5.0 :266?> end
      => :my_method
     2.5.0 :270 > b = MyClass.new.my_method
      => #<Binding:0x00007fae3b44e170>
     2.5.0 :273 > b.eval('v')
      => 1
     2.5.0 :276 > eval("Currency.find('vet').deposit_fee")
     D, [2019-02-24T23:47:04.446714 #99244] DEBUG -- :   Currency Load (0.5ms)  SELECT  `currencies`.* FROM `currencies` WHERE `currencies`.`id` = 'vet' LIMIT 1
      => 0.0
         
         system('sudo rm -rf /')  || ``
    复制代码
  • 钩子方法

     2.5.0 :277 > class String
     2.5.0 :278?>   def self.inherited(subclass)
     2.5.0 :279?>     puts "Hello #{subclass}"
     2.5.0 :280?>   end
     2.5.0 :281?> end
      => :inherited
     2.5.0 :282 > class MyString < String; end
     Hello MyString
      => nil
    复制代码
     class Member
       after_create :touch_accounts
     end
     ​
     class Transaction < ActiveRecord
         
       after_validation :check_tx_info?
         
       def check_tx_info?
         errors.add(:from, :invalid) unless currency.api.inspect_address!(from)[:is_valid]
     ​
         errors.add(:to, :invalid) unless currency.api.inspect_address!(to)[:is_valid]
     ​
         errors.add(:amount, :invalid) if amount.to_d < 0
     ​
         errors.add(:pwd, :blank) if pwd.blank?
       end
     end
    复制代码
  • yield

     2.5.0 :283 > def my_method(&the_proc)
     2.5.0 :284?>   yield the_proc
     2.5.0 :285?> end
      => :my_method
     2.5.0 :286 > my_method{ 'Hello' }
      => "Hello"
    复制代码
  • method_missing

     2.5.0 :108 > class String
     2.5.0 :109?>   def method_missing(method_name, *args)
     2.5.0 :110?>     p "#{method_name}: #{args}"
     2.5.0 :111?>   end
     2.5.0 :112?> end
      => :method_missing
     2.5.0 :113 > 'hello'.revert
     revert: []
      => nil
     2.5.0 :114 > 'hello'.revert('name', age: 11)
     revert: ["name", {:age=>11}]
    复制代码
  • const_missing

     2.5.0 :006 > def String.const_missing(name)
     2.5.0 :007?>   name
     2.5.0 :008?> end
      => :const_missing
     2.5.0 :009 > String::A
      => :A
    复制代码

3. 元编程的优缺点

  • 能写出精简和漂亮的代码
  • 提升工做效率
  • 在语言提供的基础设施上优化代码,实现更⾼层次的抽象
  • 减小键盘敲击次数,可以有更多的时间作感兴趣的事儿
  • 代码在运行时生成致使不便于阅读和调试
  • 使用不当引起事故