Ruby知識積累2

  • 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章