Ruby On Rails--rake 任务中定义方法的陷阱

来源:互联网 发布:淘宝组装电脑都在武汉 编辑:程序博客网 时间:2024/05/29 14:39

问题描述

在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.rakenamespace :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  endend#sample_app/lib/tasks/handle_old_data/hanlde_old_users.rakenamespace :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  endend

在命令行执行 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.rakenamespace :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  endend#sample_app/lib/tasks/handle_old_data/hanlde_old_users.rakenamespace :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  endend

这样,这两个方法就相互不影响。

在命令行执行 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

0 0
原创粉丝点击