- Block:
Proc.new my_proc = Proc.new { puts "tweet" } my_proc.call lambda my_proc = lambda { puts "tweet" } my_proc.call -> my_proc = -> { puts "tweet" } my_proc.call Parameter: e.g. class Tweet def post(success, error) if authenticate?(@user, @password) success.call else error.call end end end tweet = Tweet.new('Ruby Bits!') success = -> { puts "Sent!" } error = -> { raise 'Auth Error' } tweet.post(success, error) e.g. tweets = ["First tweet", "Second tweet"] printer = lambda { |tweet| puts tweet } tweets.each(&printer) # &操作符將lambda轉換爲block block_given? e.g. class Timeline attr_accessor :tweets def print if block_given? tweets.each { |tweet| puts yield tweet } else puts tweets.join(', ') end end end timeline = Timeline.new timeline.tweets = ["One", "Two"] timeline.print timeline.print { |tweet| "tweet: #{tweet}" } e.g. class Tweet def initialize yield self if block_given? end end Tweet.new do |tweet| tweet.status = "Set in initialize!" tweet.created_at = Time.now end closure(閉包): def tweet_as(user) lambda { |tweet| puts "#{user}: #{tweet}" } end gregg_tweet = tweet_as("andy") greeg_tweet.call("Mind blowing!") Symbol#to_proc tweets.map { |e| tweet.user } tweets.map(&:user)
- Struct:
e.g. 類: class Tweet attr_accessor :user, :status def initialize(user, status) @user, @status = user, status end def to_s "#{user}: #{status}" end end 結構體: Tweet = Struct.new(:user, :status) do def to_s "#{user}: #{status}" end end
- Alias_method:
Bad: class Timeline def initialize(tweets = []) @tweets = tweets end def tweets @tweets end def contents @tweets end end Good: class Timeline def initialize(tweets = []) @tweets = tweets end attr_reader :tweets alias_method :contents, :tweets end
- Define_method:
e.g. native: class Tweet def draft @status = :draft end def posted @status = :posted end def deleted @status = :deleted end end change: class Tweet status = [:draft, :posted, :deleted] status.each do |status| define_method status do @status = status end end end
- Send:(常用於訪問protected和private方法)
e.g. class Timeline def initialize(tweets) @tweets = tweets end def contents @tweets end private def direct_message #... end end tweets = ['Compiling!', 'Bundling...'] timeline = Timeline.new(tweets) timeline.contents timeline.send(:contents) timeline.send("contents") timeline.send(:direct_message) timeline.public_send("direct_message") # only can access public methods
- Method:
e.g. class Timeline def initialize(tweets) @tweets = tweets end def contents @tweets end def show_tweet(index) puts @tweets[index] end end tweets = ['Compiling!', 'Bundling...'] timeline = Timeline.new(tweets) content_method = timeline.method(:contents) content_method.call show_tweet = timeline.method(:show_tweet) show_tweet.call(0) (0..1).each(&show_tweet) #相當於show_tweet(0)和show_tweet(1)
- Self:
e.g. puts "Outside the class: #{self}" # main class Tweet puts "Inside the class: #{self}" # Tweet def self.find(keyword) # ... end end
- Class_eval(在指定類中執行):
e.g. class Tweet attr_accessor :status attr_reader :created_at def initialize(status) @status = status @created_at = Time.now end end Tweet.class_eval do attr_accessor :user end tweet = Tweet.new("Learning class_eval") tweet.user = "doit"
- Create a log_method:
log_method以類名和方法名作爲參數,調用class_eval來執行類中的代碼。使用alias_method來保存原先的方法,使用define_method來重定義方法,對該方法的調用過程進行記錄,使用send來調用原先的方法。 class MethodLogger def log_method(klass, method_name) klass.class_eval do alias_method "#{method_name}_original", method_name define_method method_name do |*args, &block| puts "#{Time.now}: Called #{method_name}" send "#{method_name}_original", *args, &block end end end end e.g. class Tweet def say_hi puts "hi" end end logger = MethodLogger.new logger.log_method(Tweet, :say_hi) Tweet.new.say_hi
- Instance_eval:
e.g. class Tweet attr_accessor :user, :status end tweet = Tweet.new tweet.instance_eval do self.status = "Changing the tweet's status" end Good: class Tweet attr_accessor :user, :status def initialize yield self if block_given? end end Tweet.new do |tweet| tweet.status = "I was set in the initialize block!" tweet.user = "Gregg" end Better: class Tweet attr_accessor :user, :status def initialize(&block) instance_eval(&block) if block_given? end end
- Method_missing:
當無法找到方法時調用 e.g. class Tweet def method_missing(method_name, *args) logger.warn "You tried to call #{method_name} with these arguments: #{args}" super # ruby's default method handling raise a NoMethodError end end e.g.(對一些方法進行委託) class Tweet DELEGATED_METHODS = [:username, :avatar] def initialize(user) @user = user end def method_missing(method_name, *args) if DELEGATED_METHODS.include?(method_name) @user.send(method_name, *args) else super end end end e.g.(將未知方法,全部委託到user) require 'delegate' class Tweet < SimpleDelegator def initialize(user) super(user) end end e.g. class Tweet def initialize(text) @text = text end def to_s @text end def method_missing(method_name, *args) match = method_name.to_s.match(/^hash_(\w+)/) if match @text << " #" + match[1] else super end end end e.g.(define_method) class Tweet def initialize(text) @text = text end def to_s @text end def method_missing(method_name, *args) match = method_name.to_s.match(/^hash_(\w+)/) if match self.class.class_eval do define_method(method_name) do @text << " #" + match[1] end end send(method_name) else super end end end
- Response_to?:
e.g. class Tweet ... def respond_to?(method_name) method_name =~ /^hash_\w+/ ||super end end tweet = Tweet.new tweet.respond_to?(:to_s) tweet.respond_to?(:hash_ruby) tweet.method(:hash_ruby) # NameError:undefined method
- Response_to_missing?:(Ruby 1.9.3)
e.g. class Tweet ... def respond_to_missing?(method_name) method_name =~ /^hash_\w+/ ||super end end tweet = Tweet.new tweet.method(:hash_ruby) # return a Method object
- Implement a simple DSL:
DSL:領域專用語言 e.g. class Tweet def initialize(user) @user = user @tweets = [] @annotations = {} end def submit_to_twitter tweet_text = @tweet.join(' ') if tweet_text.length <= 140 puts "#{@user}: #{tweet_text}" puts @annotations.inspect unless @annotations.empty? else raise "your tweet is too long." end end def text(str) @tweet << str self end def mention(*users) users.each do |user| @tweet << "@" + user end self end def hashtag(str) @tweet << "#" + str self end def link(str) @tweet << str end def method_missing(method, *args) @annotations[method] = args.join(', ') end end def tweet_as(user, text=nil,&block) tweet = Tweet.new(user) tweet.text(text) if text tweet.instance_eval(&block) if block_given? tweet.submit_to_twitter end tweet_as 'marken' do mention 'china','usa' text('I made a DSL!').hashtag('hooray').hashtag('ruby') link 'http://www.somewhere.com' end
Ruby知識積累2
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.