【问题标题】:def block in rake taskrake 任务中的 def 块
【发布时间】:2011-11-10 00:24:17
【问题描述】:

我通过以下 rake 任务获得了 undefined local variable or method 'address_geo' for main:Object。它有什么问题?

include Geokit::Geocoders

namespace :geocode do
  desc "Geocode to get latitude, longitude and address"
  task :all => :environment do
    @spot = Spot.find(:first)
    if @spot.latitude.blank? && !@spot.address.blank?
      puts address_geo
    end

    def address_geo
      arr = []
      arr << address if @spot.address
      arr << city if @spot.city
      arr << country if @spot.country
      arr.reject{|y|y==""}.join(", ")
    end
  end
end

【问题讨论】:

  • 看起来您在定义函数之前调用了它 - 您是否尝试将 address_geo 的函数定义移动到任务的顶部?
  • 是的,试过了,但没用。但我想我不再需要这个了。谢谢。
  • 大多数时候我想在 rake 任务中定义一个方法,我发现通过创建一个新的 rake 任务并调用它可以更好地解决问题:Rake::Task['cache:clear'].invoke。它更易于阅读和维护,您可以单独运行rake cache:clear

标签: ruby-on-rails ruby methods rake


【解决方案1】:

您可以使用Proc 来获得相同的效果,而无需声明全局方法。例如,

include Geokit::Geocoders

namespace :geocode do
  desc "Geocode to get latitude, longitude and address"
  task :all => :environment do

    address_geo = Proc.new do
      arr = []
      arr << address if @spot.address
      arr << city if @spot.city
      arr << country if @spot.country
      arr.reject{|y|y==""}.join(", ")
    end
    @spot = Spot.find(:first)
    if @spot.latitude.blank? && !@spot.address.blank?
      puts address_geo.call
    end
  end
end

【讨论】:

    【解决方案2】:

    更新:明白了

    这可能会将该方法添加到全局范围,并且会与任何其他同名方法发生冲突。查看@Hula_Zell 的答案https://stackoverflow.com/a/44294243/584440 以获得更好的方法。

    原答案

    您正在 rake 任务中定义方法。为了获得该功能,您应该在 rake 任务之外(在任务块之外)进行定义。试试这个:

    include Geokit::Geocoders
    
    namespace :geocode do
      desc "Geocode to get latitude, longitude and address"
      task :all => :environment do
        @spot = Spot.find(:first)
        if @spot.latitude.blank? && !@spot.address.blank?
          puts address_geo
        end
      end
    
      def address_geo
        arr = []
        arr << address if @spot.address
        arr << city if @spot.city
        arr << country if @spot.country
        arr.reject{|y|y==""}.join(", ")
      end
    end
    

    【讨论】:

    • 我正在这样做,我的方法可用于所有 rake 任务,你知道我该如何避免这种情况吗?
    • @WilliamWeckl 您可以在 rake 任务中定义 lambdas。
    • @justingordon 你是什么意思?怎么办?
    • 此方法在所有 rake 任务中都可用,但并不安全。 Hula_Zell 的解决方案更好。
    • 谢谢@jamesdevar。我没有意识到这一点。我想,因为它在命名空间内,所以它只能用于命名空间。看起来 rake 命名空间仅适用于 rake 任务。
    【解决方案3】:

    小心:rake 文件中定义的方法最终定义在全局命名空间中。

    我建议将方法提取到模块或类中。这是因为在 rake 文件中定义的方法最终会在全局命名空间中定义。也就是说,它们可以从任何地方调用,而不仅仅是在该 rake 文件中(即使它是命名空间的!)。

    这也意味着如果你在两个不同的 rake 任务中有两个同名的方法,其中一个会在你不知情的情况下被覆盖。非常致命。

    这里有一个很好的解释:https://kevinjalbert.com/defined_methods-in-rake-tasks-you-re-gonna-have-a-bad-time/

    【讨论】:

    • 我也认为这是将方法提取到模块或类中的最佳实践。
    • 加一。我没有意识到这一点。
    • 我第一次来这里时没有花时间阅读这篇文章,结果它咬了我一口。每个人都应该知道这种潜在的冲突,要么非常小心,要么将方法提取到可能需要的模块中。
    猜你喜欢
    • 2010-10-09
    • 1970-01-01
    • 1970-01-01
    • 2013-06-15
    • 2011-12-03
    • 2020-09-07
    • 2012-11-15
    • 2017-10-04
    • 1970-01-01
    相关资源
    最近更新 更多