【问题标题】:Ruby 1.9.2: How to change scope/binding of a blockRuby 1.9.2:如何更改块的范围/绑定
【发布时间】:2012-01-25 08:37:29
【问题描述】:

您好,我有类似以下内容:

class TrialRequest

  attr_accessor :trial_email

  def initialize(email)
    @trial_email = email
    puts "Trial_email: #{trial_email}"
  end

  def create
    @email = ::Gmail.connect!(gmail_name, gmail_password) do |gmail|
      email = gmail.compose do
        to 'trial@domain.com'
        from trial_email
        subject trial_email
        text_part do
          content_type 'text/html; charset=UTF-8'
          body 'Sign me up.'
        end
      end
      #binding.pry
      gmail.deliver!(email)
    end
  end
end

问题是compose块内部trial_email没有定义:

NameError: undefined local variable or method `trial_email' for #<Mail::Message:0x0000000431b830>

这是 Ruby 1.9 问题还是 gmail gem 问题?

我应该如何让这个方法“可见”/在 compose 块的范围内?

更新: 这是 gmail gem 的一个问题/功能 - ruby​​ 1.9 块已经改变,但没有这么多! 除了接受的答案之外,另一种解决方法是将数据作为方法参数传递:

def create(trial_email)
  ...
end

【问题讨论】:

    标签: ruby block ruby-1.9.2


    【解决方案1】:

    对我来说似乎是 GMail 问题。在这些块中,self 将是来自 GMail gem 的一些对象,因此您可以使用 tofrom 和类似的 DSL 细节。您应该能够将self.trial_email 放入一个局部变量中,然后在块内访问它:

    email_address = self.trial_email
    @email = ::Gmail.connect!(gmail_name, gmail_password) do |gmail|
      email = gmail.compose do
        to 'trial@domain.com'
        from email_address
        subject email_address
        #...
    

    【讨论】:

    • 我通过让 create 接受一个参数来实现它。我认为这与您的建议具有相同的效果。对吗?
    • 这不是问题,是instance_eval的限制,不是吗?
    • @Hedgehog:差不多,这个参数代替了我的email_address局部变量。
    • @KL-7:我不确定你的意思。
    • @muistooshort,您可以从一个块中引用创建该块的上下文的所有局部变量。但是在 OP 的示例中,trial_email 实际上是一个不同对象的方法,因此它不会被块“记住”作为其上下文的一部分。这就是为什么当这个块被传递给其他对象instance_eval 时,对trial_email 的引用会丢失。但是如果你用局部变量块替换它仍然可以使用它,因为它现在是块上下文的一部分。
    【解决方案2】:

    您期望(您有权)该块应该保留 self 的值,就像它通常所做的那样。看起来 gmail gem 在这里使用 instance_exec ,这允许它将块的 self 值更改为 Mail::Message 的实例(这就是为什么你可以在该块中调用 tofrom 即使你没有定义这样的方法)

    虽然instance_exec 可以方便地生成漂亮的 DSL,但它并非没有缺点。局部变量作用域不受影响,因此您可以将 trial_emailself 存储在块之前的局部变量中,然后在块内使用该局部变量

    【讨论】:

      【解决方案3】:

      问题是你传递给compose方法的块后来被传递给Mail.new,最后被传递给Message.new(如果我正确地跟踪了链)然后这个块被评估为here

      instance_eval(&block)
      

      由于它在不同对象(Message 类的实例)的 initialize 方法中执行,因此您无权访问您的 TrialRequest 对象的属性。

      您可以做同样的事情而不会遇到任何麻烦:

      email = gmail.compose
      email.to = 'trial@domain.com'
      email.from = trial_email
      email.subject = trial_email
      
      email.text_part do
        content_type 'text/html; charset=UTF-8'
        body 'Sign me up.'
      end
      

      【讨论】:

        猜你喜欢
        • 2015-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-27
        • 2011-08-16
        • 2010-11-06
        • 2017-10-10
        • 1970-01-01
        相关资源
        最近更新 更多