2013-01-11

Rails:如何用最快的方式改寫gem?

hmm...Ruby / Rails很多時候都要用gem,可是很多時候都天不從人願,都需要改寫某個部分才會變成自己需要的東西,用fork太煩,要打一票merge,還不會自動更新,有的沒的缺點一大堆

Ruby是Script Language,所有lib都是有載入順序的,最方便改寫一個特定code的方式就是用mask

例如這段code:

def x(a)
  puts a
end

> x(1)
#=> 1

def x(a)
  puts a + 1
end

> x(1)
#=> 2

okay,這就是mask的真義,之後load的code會蓋掉之前的code,而真實世界怎樣辦到?

Rails下會先require gem後,才會執行config/initializers,所以其實在裡面新增.rb檔,裡面寫gem的對象,其實就可以把該gem的該methods mask掉...類似這個我自己重寫過的will_paginate.rb

看之前請對照原source,其實你會發覺我只是想把該code增加一行指令而已(下面的add this line),然後也請不要整個檔案都copy過去,而是把該method的所有上層宣告含入,其他的則全部刪除即可(就只會蓋到它)

# encoding:utf-8

module WillPaginate
  module ViewHelpers
    def will_paginate(collection, options = {})
      # early exit if there is nothing to render
      return nil unless collection.total_pages > 1

      options = WillPaginate::ViewHelpers.pagination_options.merge(options)
      options[:previous_label] ||= will_paginate_translate(:previous_label) { '← Previous' }
      options[:next_label]     ||= will_paginate_translate(:next_label) { 'Next →' }
   
      #########add this line
      options[:next_label] += "(#{collection.total_entries}#{will_paginate_translate(:unit_record)}/#{will_paginate_translate(:unit_page)}#{collection.per_page})" if options[:show_total]
      #########add this line

      # get the renderer instance
      renderer = case options[:renderer]
      when nil
        raise ArgumentError, ":renderer not specified"
      when String
        klass = if options[:renderer].respond_to? :constantize then options[:renderer].constantize
          else Object.const_get(options[:renderer]) # poor man's constantize
          end
        klass.new
      when Class then options[:renderer].new
      else options[:renderer]
      end
      # render HTML for pagination
      renderer.prepare collection, options, self
      renderer.to_html
    end
  end
end

這行的作用其實是在『下一頁』這按鈕上面,改成『下一頁(12307筆/每頁30)』,也就是增加total和per page的文字說明(當然這邊有另外作i18n就是了)

so...沒有fork,只有mask,當然方便的風險也還是有的,如果未來這個gem大量改寫了,那該code可能會掛掉,以前就曾經幫paperclip寫video process lib(video => flv)就曾出現這類的事情X"D...不過你會發覺,其實只要再去查source,然後一樣mask過去即可,比起完整的fork去處理...應該簡潔省事得多唄...:)

沒有留言:

張貼留言