如何重构几次使用`instance_exec`的代码?

时间:2015-09-24 05:38:19

标签: ruby-on-rails ruby yield instance-eval

我正在使用Prawn gem生成PDF的类。我有一些类似的方法。所有这些都以同一行开头。这是代码:

module PDFGenerator
  class MatchTeamInfo
    include Prawn::View

    def initialize(match)
      @match = match
      @output = Prawn::Document.new page_layout: :landscape
      defaults
      header
      footer
    end

    def render
      @output.render
    end

    def defaults
      @output.instance_exec do
        font_size 16
        text 'hola'
      end
    end

    def header
      @output.instance_exec do
        bounding_box [bounds.left, bounds.top], :width  => bounds.width do
          text "Fulbo", align: :center, size: 32
          stroke_horizontal_rule
          move_down(5)
        end
      end
    end

    def footer
      @output.instance_exec do
        bounding_box [bounds.left, bounds.bottom + 25], :width  => bounds.width do
          stroke_horizontal_rule
          move_down(5)
          text "Tu sitio favorito!", align: :center
        end
      end
    end
  end
end

有没有办法在每种方法中避免使用@output.instance_exec并使用像块一样的东西?我尝试过,但我无法让它发挥作用。我可以这样做吗?

def apply
  @output.instance_exec do
    yield
  end
end

我该如何定义代码块?

2 个答案:

答案 0 :(得分:1)

首先,您需要使所有辅助方法返回lambda实例:

def defaults
  lambda do
    font_size 16
    text 'hola'
  end
end

现在您可以将助手返回的lambda传递给instance_exec。要承认它“这是代码块而不是常规参数”,lambda将以&符为前缀:

def apply
  #                     ⇓ NB! codeblock is passed!
  @output.instance_exec &defaults
end

如果您想将代码块传递给apply,则应将其重新传递给instance_exec。不幸的是我知道无法使用yield关键字重新传递它,但是这里有一个技巧:Proc.new在使用给定的代码块调用的方法中调用没有参数,使用此代码块进行实例化,因此你走了:

def apply
  raise 'Codeblock expected' unless block_given?
  @output.instance_exec &Proc.new
end

答案 1 :(得分:1)

您可以定义返回document实例的方法Prawn::Document

Prawn::View然后将方法调用委托给该文档。这是一个例子:

module PDFGenerator
  class MatchTeamInfo
    include Prawn::View

    def initialize(match)
      @match = match
      defaults
      header
      footer
    end

    def document
      @document ||= Prawn::Document.new page_layout: :landscape
    end

    def defaults
      font_size 16
      text 'hola'
    end

    def header
      bounding_box [bounds.left, bounds.top], :width  => bounds.width do
        text "Fulbo", align: :center, size: 32
        stroke_horizontal_rule
        move_down(5)
      end
    end

    def footer
      bounding_box [bounds.left, bounds.bottom + 25], :width  => bounds.width do
        stroke_horizontal_rule
        move_down(5)
        text "Tu sitio favorito!", align: :center
      end
    end
  end
end

使用示例:

pdf = PDFGenerator::MatchTeamInfo.new(nil)
pdf.save_as('team_info.pdf')

输出:(转换为PNG)

team_info.pdf