how to require my library in chef ruby_block
Asked Answered
L

1

17

I'm developing a cookbook to deploy a simple ROR application. I write an app_helper.rb and put it into the libraries directory of my cookbook, here is the content:

module AppHelper

    def self.find_gem
      if File.exists?("/usr/local/rvm/bin/rvm")
        return `/usr/local/rvm/bin/rvm default exec which gem`.chomp
      else
        return "/usr/bin/gem"
      end
    end
  end

In the recipes/default.rb, I mix in the above module into Chef::Recipe class

class Chef::Recipe
  include AppHelper
end

As you know, the find_gem function can be called from anywhere of the recipe.

when I'm trying to use the find_gem function in my ruby_block like this:

ruby_block "find gem" do
   block do
    gem_bin = Chef::Recipe::find_gem
    # or gem_bin = find_gem
  end
end

I got a NoMethodError: undefined method 'find_gem'.

Also try to mix in the module into Chef::Resource::RubyBlock, it doesn't work neither.

class Chef::Resource::RubyBlock
  include AppHelper
end

ruby_block "find gem" do
   block do
    gem_bin = Chef::Resource::RubyBlock::find_gem
    # or gem_bin = find_gem
  end
end

Is there any way to call the function in the module from the ruby_block? Or Is there an variable of chef to location the files in the libraries, so that I can be able to require the module in the ruby_block.

Thanks!

Lingerfelt answered 30/12, 2013 at 8:5 Comment(3)
Make sure you mix you module into Chef::Resource and not into Chef::Recipe as your code actually says.Grillwork
Oops...It's my typo. It should be Chef::Recipe. Actually, I have tried mix the module into Chef::Resource::RubyBlock.Lingerfelt
What about calling the method through dot? Chef::Resource::RubyBlock.find_gemGrillwork
N
20

Depending on load order, you might be including your module into an anonymous Ruby class instead of what you think.

If you want to use your method in a recipe, do this at the top of your recipe:

include AppHelper

You could alternatively use :send at the end of your library:

Chef::Recipe.send(:include, AppHelper)

This is different because it will raise an exception if Chef::Recipe is not defined (whereas you are creating a new class if it doesn't exist).

That's all you should need to do unless you want to use the helper in not_if and only_if guards. Then you need to include the helper in the resource:

Chef::Resource.send(:include, AppHelper)

Okay, now that I explained all of that, it won't actually help you. The Ruby Block provider simply calls the block - it doesn't instance eval it. So including the helper in the resource doesn't do anything.

So, you'll need to use a singleton object in this instance (it's the only solution I can reliably think of). The way you've defined your method, you can call it directly from the global namespace:

AppHelper.find_gem('...')

So:

ruby_block "find gem" do
  block do
    gem_bin = AppHelper.find_gem('...')
  end
end
Nestornestorian answered 30/12, 2013 at 16:43 Comment(4)
Thank you very much for your answer. It really helped me to understand. The library defined in the libraries is automatically required and will be available in the context of this cookbook, as you said, it can be called from the global namespace.Lingerfelt
By the way, I don't have 15 reputation to vote up your answer, Sorry about that.Lingerfelt
Heh. It's fine - it'll come soon :)Nestornestorian
@Lingerfelt if you find yourself needing to use gems, have a look at my blog post: sethvargo.com/using-gems-with-chefNestornestorian

© 2022 - 2024 — McMap. All rights reserved.