Including recipes vs normal ruby require/include


#1

Chef folks,

I have a question about code reuse in Chef.

I know it’s not unusual for a recipe to include another recipe using the
“include recipe” construct. But why is this better than using the normal
Ruby mechanisms for code reuse? Couldn’t I organize my reusable bits of Chef
code into ordinary Ruby modules, and then use the ordinary Ruby require and
include tools to pull in the reusable bits as needed? My prejudice is to stick
to the normal Ruby way unless there’s a compelling reason not to.

Jeff


#2

On Thu, Feb 16, 2012 at 10:09 AM, jeff.stroomer@disney.com wrote:

Chef folks,

I have a question about code reuse in Chef.

I know it’s not unusual for a recipe to include another recipe using the
“include recipe” construct. But why is this better than using the normal
Ruby mechanisms for code reuse? Couldn’t I organize my reusable bits of Chef
code into ordinary Ruby modules, and then use the ordinary Ruby require and
include tools to pull in the reusable bits as needed? My prejudice is to stick
to the normal Ruby way unless there’s a compelling reason not to.

Jeff

Jeff,

You’re probably going to want to investigate LWRPs if you haven’t
already. That’s really the code reuse mechanism in Chef.

You don’t even have to use the resources part of it. You can just
package stuff up in a library and distribute it as a required
cookbook. See the openssl cookbook:

https://github.com/opscode/cookbooks/tree/master/openssl

as an example.

John E. Vincent


#3

you totally can build out your own modules in cookbook/libraries and
mix into the various chef resources as you need.

include_recipe provides you with a way to pull in and execute other
recipes at that point in the current recipe. As well as make sure any
defines etc are available. Tho i think the latter is better handled in
the metadata with depends clauses.

On Thu, Feb 16, 2012 at 7:09 AM, jeff.stroomer@disney.com wrote:

Chef folks,

I have a question about code reuse in Chef.

I know it’s not unusual for a recipe to include another recipe using the
“include recipe” construct. But why is this better than using the normal
Ruby mechanisms for code reuse? Couldn’t I organize my reusable bits of Chef
code into ordinary Ruby modules, and then use the ordinary Ruby require and
include tools to pull in the reusable bits as needed? My prejudice is to stick
to the normal Ruby way unless there’s a compelling reason not to.

Jeff


#4

On Thursday, February 16, 2012 at 7:09 AM, jeff.stroomer@disney.com wrote:

Chef folks,

I have a question about code reuse in Chef.

I know it’s not unusual for a recipe to include another recipe using the
“include recipe” construct. But why is this better than using the normal
Ruby mechanisms for code reuse? Couldn’t I organize my reusable bits of Chef
code into ordinary Ruby modules, and then use the ordinary Ruby require and
include tools to pull in the reusable bits as needed? My prejudice is to stick
to the normal Ruby way unless there’s a compelling reason not to.

Jeff
include_recipe contains chef-specific logic that Kernel.require and Kernel.load don’t know about. For example, within a single chef run, you don’t want to load duplicate recipes, so you want behavior similar to Kernel.require(). However, if you run chef as a daemon, you want that file to be loaded on the next chef run, which Kernel.require() will refuse to do, since it’s already seen the file within the lifetime of the ruby process.

Also, recipe files are meant to be evaluated in a certain context. Resource declarations are looked up via method_missing to create resources in the resource collection. For this to work, recipes have to be evaluated in a context where the “recipe method_missing”[1] will be called. However, when you load a file with, say, Kernel.load(), the files are evaluated in the context of the main object. For example:

ruby-1.9.3-p0 :003 > `cat /tmp/context.rb`
=> "puts self.inspect\n"  




ruby-1.9.3-p0 :004 > o = Object.new
=> #<Object:0x007fc5c9880dd8>  
ruby-1.9.3-p0 :005 > o.instance_eval { load "/tmp/context.rb" }
main

Finally, be aware that while it’s great that you can use ruby to get at Chef’s internals and make it do things no one thought of before, the further you stray from the standard ways of doing things, the more likely you are to be broken by otherwise harmless changes.

HTH,


Dan DeLeo

  1. https://github.com/opscode/chef/blob/master/chef/lib/chef/mixin/recipe_definition_dsl_core.rb