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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值