在Ruby中使用块的顺序是什么

Den*_*sen 5 ruby block

我正在创建一个gem来支持命令行中的一些邮件.我用了一些宝石.我正在使用Mail Gem.正如你在描述中看到的那样mail gem是这样的.

mail = Mail.new do
  from    'mikel@test.lindsaar.net'
  to      'you@test.lindsaar.net'
  subject 'This is a test email'
  body    File.read('body.txt')
end
Run Code Online (Sandbox Code Playgroud)

在块中,我从Mail类中调用方法(from,to,subject,body).这是有道理的,所以我在我自己的邮件程序类中构建它

def initialize(mail_settings, working_hours)
  @mail_settings = mail_settings
  @working_hours = working_hours
  @mailer = Mail.new do
    to mail_settings[:to]
    from mail_settings[:from]
    subject mail_settings[:subject]
    body "Start #{working_hours[:start]} \n\
          Ende #{working_hours[:end]}\n\
          Pause #{working_hours[:pause]}"
  end
end
Run Code Online (Sandbox Code Playgroud)

这看起来很直接.只需调用块并填写我通过构造函数获取的值.现在是我的问题.

我试图把邮件的主体结构分成一个单独的方法.但我不能Mail在gem 的构造函数中使用它.

module BossMailer
  class Mailer
  def initialize(mail_settings, working_hours)
    @mail_settings = mail_settings
    @working_hours = working_hours
    @mailer = Mail.new do
      to mail_settings[:to]
      from mail_settings[:from]
      subject mail_settings[:subject]
      body mail_body
    end
  end

  def mail
    @mailer.delivery_method :smtp, address: "localhost", port: 1025
    @mailer.deliver
  end

  def mail_body
    "Start #{working_hours[:start]} \n\
    Ende #{working_hours[:end]}\n\
    Pause #{working_hours[:pause]}"
  end
end
Run Code Online (Sandbox Code Playgroud)

结束

此错误出现在此代码中. 在此输入图像描述

这意味着我不能@a在这个块中使用我的类方法或类变量(以...开头).

问题

块中执行的顺序是什么?如果我设置我的变量@mail_settings,我不能在块中使用它.Ruby @mail_settingsMail类中我在哪里搜索块?为什么我可以BossMailer::Mailer通过块使用构造函数中的给定参数,并且不会出现错误?

如果我使用和变量将内容解析为块,为什么这会起作用?(body_content = mail_body)有效!

def initialize(mail_settings, working_hours)
  @mail_settings = mail_settings
  @working_hours = working_hours
  body_content = mail_body
  @mailer = Mail.new do
    to mail_settings[:to]
    from mail_settings[:from]
    subject mail_settings[:subject]
    body body_content
  end
end
Run Code Online (Sandbox Code Playgroud)

Ser*_*sev 3

这都是关于上下文的。

mail = Mail.new do
  from    'mikel@test.lindsaar.net'
  to      'you@test.lindsaar.net'
  subject 'This is a test email'
  body    File.read('body.txt')
end
Run Code Online (Sandbox Code Playgroud)

fromto方法(以及其余的)是Mail::Message实例上的方法。为了能够以这种良好的 DSL 方式调用它们,传递给构造函数的块是instance_eval'ed

这意味着在这个块内,self不再是您的邮件程序,而是邮件消息。因此,您的邮件程序方法无法访问。

instance_eval他们可以只使用yield或来代替block.call,但这不会使 DSL 成为可能。

至于局部变量起作用的原因:这是因为 ruby​​ 块是词法范围的闭包(这意味着,它们保留其声明的局部上下文。如果有一个从定义块的位置可见的局部变量,它会记住该变量及其调用块时的值)

替代方法

不要使用块形式。使用这个: https: //github.com/mikel/mail/blob/0f9393bb3ef1344aa76d6dac28db3a4934c65087/lib/mail/message.rb#L92-L96

mail = Mail.new
mail['from'] = 'mikel@test.lindsaar.net'
mail[:to]    = 'you@test.lindsaar.net'
mail.subject 'This is a test email'
mail.body    = 'This is a body'
Run Code Online (Sandbox Code Playgroud)

代码

尝试注释/取消注释某些行。

class Mail
  def initialize(&block)
    # block.call(self) # breaks DSL
    instance_eval(&block) # disconnects methods of mailer
  end

  def to(email)
    puts "sending to #{email}"
  end

end

class Mailer
  def admin_mail
    # get_recipient = 'vasya@example.com'
    Mail.new do
      to get_recipient
    end
  end

  def get_recipient
    'sergio@example.com'
  end
end


Mailer.new.admin_mail
Run Code Online (Sandbox Code Playgroud)