Topics
• What is and Why Meta-programming?
• Ruby language characteristics (that make it a great meta-programming language)
• Introspection
• Object#send
• Dynamic typing (Duck typing)
• missing_method
• define_method
What is Meta-Programming?
什麼是Meta-Programming
• Metaprogramming is the writing of computer programs that write or manipulate other programs (or themselves) as their data
Metaprogramming是一種電腦程序編寫方式,把編寫或操作程序作爲數據
Why Meta-Programming?
• Provides higher-level abstraction of logic
可以有更高層次的邏輯抽象
> Easier to write code
更簡單的寫代碼
> Easier to read code
更容易閱讀代碼
• Metaprogramming feature of Ruby language is what makes Rails a killer application.
Ruby的Metaprogramming特性使Rails更加優秀。
> For example, the Rails declarations such as "find_by_name","belongs_to" are possible because of the Meta-programming feature of Ruby language.
例如,由於Ruby的Meta-programming特性可以使Rails如此聲明,"find_by_name","belongs_to".
Ruby Language Characteristics that Make It a Great Meta-Programming Language
Ruby Language Characteristics
Ruby語言特性
• Classes are open
類是開放的
• Class definitions are executable code
類可擴展
• Every method call has a receiver
每一個方法調用都有接收者
• Classes are objects
類是對象化的
Classes Are Open
• Unlike, Java and C++, methods and instance variables can be added to a class (including core classes provided by Ruby such as String and Fixnum) during runtime.
不同與java和C++,在Ruby裏,方法,實例變量能在運行時添加到一個類裏(包括Ruby的核心類,例如String和Fixnum)
• Example: Define a new method for String class
例:給String類定義一個新方法
- class String
- def encrypt
- tr "a-z","b-za"
- end
- end
- puts "cat"
- puts "cat".encrypt
好處
> Applications can be written in higher level abstraction
程序可以更加抽象的編寫
> More readable code
代碼更加可讀
> Less coding
編寫量減少
• How it is used in Rails
怎麼應用在Rails裏
> One can open up Rails classes and add new features to them.
可以打開一個Rails類,並添加新特性
> Rails integration testing
Rails集成測試
Class Definitions are Executable Code
類是可擴展的
• The log(msg) method is defined differently during runtime
log方法可以在運行時改變定義
- class Logger
- if ENV['DEBUG']
- def log(msg)
- STDERR.puts "LOG:" +msg
- end
- else
- def log(msg)
- end
- end
- end
類是對象化的
• String class is an instance of Class class in the same way Fixnum class (or whatever class) is an instance of Class class
String類是Class類的一個實例,同理,Fixnum類是Class類的一個實例
- class Person
- puts self # Person is an instance of Class
- def self.my_class_method
- puts "This is my own class method"
- end
- end
• Default receiver is self
默認的接收者就是本身
Introspection
What is Introspection?
• Being able to find information on an object during runtime
可以在運行時查找一個對象的信息
Examples
> Object#respond_to?
> Object#class
> Object#methods
> Object#class.superclass
> Object#class.ancestors
> Object#private_instance_methods()
> Object#public_instance_methods(false)
> ...
Dynamic Method Invocation through Object#send
用Object#send進行動態方法調用
• In Ruby, an object’s methods are not fixed at any compilation time but can be dynamically extended or modified at any point.
在Ruby裏,對象的方法並不一定適合編譯,但能夠動態的擴展或修改在任何地方。
• Instead of calling a method directly by name as following
替代如下的根據名字直接進行方法調用
> an_object_instance.hello(“Good morning!”)
• It is possible instead to invoke generically any object method by using a string or symbol variable to specify the target method
可以通過字符串或符號變量定義目標方法來替代對象方法的直接di
> an_object_instance.send(”#{name_of_method}”, args)
obj.send(symbol [, args...])
• Invokes the method identified by symbol, passing it any arguments specified.
調用標識符定義的方法,傳遞任意指定的參數。
- class Klass
- def hello(*args)
- "Hello " + args.join(' ')
- end
- end
- k = Klass.new
- #The following statements are equivalent
- puts k.hello("gentle", "readers") #=> "Hello gentle readers"
- puts k.hello "gentle", "readers" #=> "Hello gentle readers"
- puts k.send(:hello, "gentle", "readers")#=> "Hello gentle readers"
- puts k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
Dynamic Typing(Duck Typing)
What is Dynamic Typing?
• A programming language is said to use dynamic typing when type checking is performed at run-time (also known as "late-binding") as opposed to compile-time.
使用動態類型的程序語言在運行時檢查數據類型(又稱“懶加載”),這與編譯時相對。
> Examples of languages that use dynamic typing include PHP, Lisp, Perl, Python, Ruby, and Smalltalk.
動態語言包括PHP, Lisp, Perl, Python, Ruby, and Smalltalk.
What is Duck Typing
• Duck typing is a style of dynamic typing in which an object's current set of methods and properties determines the valid semantics, rather than its inheritance from a particular class.
Duck類型是一個對象當前的方法和屬性決定於正確的語義,而不是決定於繼承一個類。
> The name of the concept refers to the duck test, attributed to James Whitcomb Riley, which may be phrased as “If it walks like a duck and quacks like a duck, I would call it a duck”.
這個名字的含義涉及到鴨子,由James Whitcomb Riley提出,可以理解爲“如果一個東西走路像鴨子,叫聲像鴨子,那就叫它鴨子吧
”。
Duck Typing Example (page 1)
- # The Duck class
- class Duck
- def quack
- puts "Duck is quacking!"
- end
- end
- #The Mallard class
- class Mallard
- def quack
- puts "Mallard is quacking!"
- end
- end
- #If it quacks like a duck, it must be duck
- def quack_em(ducks)
- ducks.each do |duck|
- if duck.respond_to? :quack
- duck.quack
- end
- end
- end
- birds = [Duck.new, Mallard.new, Object.new]
- puts "----Call quack method for each item of the birds array.Only Duck and Mallard should be quacking."
- quack_em(birds)
missing_method
NoMethodError Exception
• If a method that is not existent is in a class is invoked,NoMethodError exception will be generated
如果一個方法在調用的函數裏不存在,將會拋出NoMethodError異常
- class Dummy
- end
- puts "----Call a method that does not exist in the Dummy class
- and expect NoMethodError exception."
- dummy = Dummy.new
- dummy.call_a_method_that_does_not_exist
• If method_missing(m, *args) method is defined in a class, it will be called (instead of NoMethodError exception being generated) when a method that does not exist is invoked
如果method_missing(m, *args)方法被定義在一個類裏,當一個不存在方法被調用時,這個方法將被調用(替代拋出NoMethodError)
- class Dummy
- def method_missing(m, *args)
- puts "There's no method called #{m} here -- so method_missing method is called."
- puts " with arguments #{args}"
- end
- end
- dummy = Dummy.new
- dummy.a_method_that_does_not_exist
How method_missing Method is used in Rails
method_missing方法如何在Rails裏應用
• Rails' find_by_xxxx() finder method is implemented through method_missing.
Rails的find_by_xxxx()方法就由method_missing實現
- class Finder
- def find(name)
- puts "find(#{name}) is called"
- end
- def method_missing(name, *args)
- if /^find_(.*)/ =~ name.to_s
- return find($1)
- end
- super
- end
- end
- f = Finder.new
- f.find("Something")
- f.find_by_last_name("Shin")
- f.find_by_title("Technology Architect")
define_method
• The define_method defines an instance method in the receiver.
define_method 定義一個實例方法在接收者。
- define_method(symbol, method) => new_method
- define_method(symbol) { block } => proc
方法參數可以是一個Proc或Method對象。如果block被指定,將被作爲方法體。
• An example of
> define_method(symbol) { block } => proc
- class Love
- define_method(:my_hello) do |arg1, arg2|
- puts "#{arg1} loves #{arg2}"
- end
- end
- love = Love.new
- love.my_hello("Sang Shin", "Young Shin")