17

在厨师食谱中做一点 DRY 的最佳方法是什么?即只是打破Ruby代码的一小部分,所以我不会一遍又一遍地复制粘贴它。

当然,以下失败:

NoMethodError: undefined method `connect_root' for Chef::Resource::RubyBlock

我可能在一个配方中有多个 ruby​​_blocks,因为它们做不同的事情并且需要有不同的 not_if 块才能成为真正的幂等。

def connect_root(root_password)
  m = Mysql.new("localhost", "root", root_password)
  begin
    yield m
  ensure
    m.close
  end
end

ruby_block "set readonly" do
  block do
    connect_root node[:mysql][:server_root_password] do |connection|
      command = 'SET GLOBAL read_only = ON'
      Chef::Log.info "#{command}"
      connection.query(command)
    end
  end
  not_if do
    ro = nil
    connect_root node[:mysql][:server_root_password] do |connection|
      connection.query("SELECT @@read_only as ro") {|r| r.each_hash {|h| 
        ro = h['ro']
      } }
    end
    ro
  end
end
4

2 回答 2

25

正如您已经知道的那样,您不能在配方中定义函数。为此提供了库。您应该在食谱的库文件夹中创建一个文件(例如mysql_helper.rb),其中包含以下内容:

module MysqlHelper
  def self.connect_root( root_password )
    m = Mysql.new("localhost", "root", root_password)
    begin
      yield m
    ensure
      m.close
    end
  end
end

必须是一个模块,而不是一个类。还请注意,我们将其定义为静态(使用 self.method_name)。然后,您将能够使用模块名称和方法名称在配方中使用此模块中定义的函数:

MysqlHelper.connect_root node[:mysql][:server_root_password] do |connection|
  [...]
end
于 2013-03-27T20:38:42.937 回答
1

作为记录,我刚刚创建了一个包含以下内容的库。但这对于一个文件中的 DRY 来说似乎有点过分了。我也不知道如何让模块使用任何其他命名空间来工作。

class Chef
  class Resource
    def connect_root(root_password)
      ...
于 2013-03-24T05:02:46.363 回答