Ruby On Rails--rake 任務中定義方法的陷阱

問題描述

在rails項目中,有時候需要導出數據、處理歷史數據等,這時用rake是很方便的。

在rake中,有時候由於邏輯比較複雜,所以我們就會分離邏輯或者需要重用代碼,這時候可能會定義方法。

但是,在rake中定義方法有一個問題:在不同rake文件和不同命名空間下定義的方法,如果存在了同名方法,那麼後來定義的方法會覆蓋掉之前定義的方法,這時候可能會導致bug。

下面我們用一個例子來具體說明下:
比如有個項目 sample_app,我們有2個rake文件:
sample_app/lib/tasks/export_data/export_users.rake
sample_app/lib/tasks/handle_old_data/hanlde_old_users.rake
在項目中排列順序和這裏列出的順序一致。

對應代碼如下:

# sample_app/lib/tasks/export_data/export_users.rake
namespace :export_data
  task export_users: :environment do
    user_data = get_user_data
    # other operations
  end

  def get_user_data
    puts "In export_data, get_user_data"
    # return user_data
  end
end

#sample_app/lib/tasks/handle_old_data/hanlde_old_users.rake
namespace :handle_old_data
  task handle_old_users: :environment do
      user_data = get_user_data
    # other operations
  end

  def get_user_data
    puts "In handle_old_data, get_user_data"
    # return user_data
  end
end

在命令行執行 rake handle_old_data:handle_old_users時會打印出:
In handle_old_data, get_user_data

這時如果在命令行執行 rake export_data:export_users ,那麼會打印出來什麼呢?
答案是:
In handle_old_data, get_user_data

也就是說,在第一個rake中調用 get_user_data 方法時,實際上是調用的 第二個rake中定義的該方法。

那麼這是爲什麼呢?

問題解答和解決方案

這個問題出現的原因,其實仔細思考一下是不難得到答案的:
lib/tasks/ 目錄下的文件是依次加載的,而且rake中的命名空間(namespace)是隻對rake任務起作用的,不會對文件中的方法起作用,所以後加載的第二個rake文件中的方法定義覆蓋掉了前一個同名方法。

那麼,怎麼解決這個問題呢?

也許有人會說,不要用同名方法不就行了嗎?

這樣當然是一種解決問題的方式。但是這樣對於開發者來說很不友好–誰會喜歡在定義方法前,去全局搜索下別人有沒有用過這個方法名?

所以,我們需要更加優雅的解決方案。

我們可以有一種方式來很好地解決這個問題:
我們在rake中定義方法時,給方法加上命名空間–可以新定義一個class或者一個module,然後將方法定義到其中;這樣相當於將我們的方法定義限制在了我們期望的範圍內。

以同時使用module和class爲例,可以這樣解決問題:

# sample_app/lib/tasks/export_data/export_users.rake
namespace :export_data
  task export_users: :environment do
    user_data = ExportData::ExportUsers.get_user_data
    # other operations
  end

  module ExportData
    class ExportUsers
      def self.get_user_data
        puts "In export_data, get_user_data"
        # return user_data
      end
    end
  end

end

#sample_app/lib/tasks/handle_old_data/hanlde_old_users.rake
namespace :handle_old_data
  task handle_old_users: :environment do
      user_data = HandleOldData::HandleOldUsers.get_user_data
    # other operations
  end

  module HandleOldData
    class HandleOldUsers
      def self.get_user_data
        puts "In handle_old_data, get_user_data"
        # return user_data
      end
    end
  end
end

這樣,這兩個方法就相互不影響。

在命令行執行 rake handle_old_data:handle_old_users 時會打印出:
In handle_old_data, get_user_data

這時如果在命令行執行 rake export_data:export_users ,那麼會打印出來:
In export_data, get_user_data

發佈了32 篇原創文章 · 獲贊 5 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章