chef-gen-flavor-base 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +6 -0
  3. data/LICENSE +17 -0
  4. data/Manifest.txt +50 -0
  5. data/README.md +818 -0
  6. data/chef-gen-flavor-base.gemspec +84 -0
  7. data/lib/chef_gen/flavor_base.rb +185 -0
  8. data/lib/chef_gen/flavor_base/copy_helpers.rb +45 -0
  9. data/lib/chef_gen/flavor_base/resource_helpers.rb +110 -0
  10. data/lib/chef_gen/snippet/actions_taken.rb +82 -0
  11. data/lib/chef_gen/snippet/attributes.rb +35 -0
  12. data/lib/chef_gen/snippet/chef_spec.rb +121 -0
  13. data/lib/chef_gen/snippet/cookbook_base.rb +135 -0
  14. data/lib/chef_gen/snippet/debugging.rb +26 -0
  15. data/lib/chef_gen/snippet/example_file.rb +36 -0
  16. data/lib/chef_gen/snippet/example_template.rb +36 -0
  17. data/lib/chef_gen/snippet/git_init.rb +36 -0
  18. data/lib/chef_gen/snippet/guard.rb +70 -0
  19. data/lib/chef_gen/snippet/next_steps.rb +44 -0
  20. data/lib/chef_gen/snippet/no_clobber.rb +62 -0
  21. data/lib/chef_gen/snippet/recipes.rb +35 -0
  22. data/lib/chef_gen/snippet/resource_provider.rb +37 -0
  23. data/lib/chef_gen/snippet/standard_ignore.rb +85 -0
  24. data/lib/chef_gen/snippet/style_foodcritic.rb +73 -0
  25. data/lib/chef_gen/snippet/style_rubocop.rb +115 -0
  26. data/lib/chef_gen/snippet/style_tailor.rb +64 -0
  27. data/lib/chef_gen/snippet/test_kitchen.rb +129 -0
  28. data/lib/chef_gen/snippet_base.rb +46 -0
  29. data/lib/chef_gen/snippets.rb +24 -0
  30. data/shared/snippet/attributes/templates/default/attributes_default_rb.erb +8 -0
  31. data/shared/snippet/chef_spec/templates/default/_rspec.erb +2 -0
  32. data/shared/snippet/chef_spec/templates/default/spec_chef_runner_context_rb.erb +36 -0
  33. data/shared/snippet/chef_spec/templates/default/spec_recipes_default_spec_rb.erb +7 -0
  34. data/shared/snippet/chef_spec/templates/default/spec_spec_helper_rb.erb +95 -0
  35. data/shared/snippet/cookbook_base/templates/default/Berksfile.erb +12 -0
  36. data/shared/snippet/cookbook_base/templates/default/CHANGELOG_md.erb +5 -0
  37. data/shared/snippet/cookbook_base/templates/default/Gemfile.erb +18 -0
  38. data/shared/snippet/cookbook_base/templates/default/README_md.erb +92 -0
  39. data/shared/snippet/cookbook_base/templates/default/Rakefile.erb +20 -0
  40. data/shared/snippet/cookbook_base/templates/default/metadata_rb.erb +13 -0
  41. data/shared/snippet/example_file/files/default/files_default_example_conf +34 -0
  42. data/shared/snippet/example_template/files/default/templates_default_example_conf_erb +22 -0
  43. data/shared/snippet/guard/templates/default/Guardfile.erb +16 -0
  44. data/shared/snippet/recipes/templates/default/recipes_default_rb.erb +9 -0
  45. data/shared/snippet/resource_provider/templates/default/providers_default_rb.erb +20 -0
  46. data/shared/snippet/resource_provider/templates/default/resources_default_rb.erb +9 -0
  47. data/shared/snippet/style_rubocop/templates/default/_rubocop_yml.erb +31 -0
  48. data/shared/snippet/test_kitchen/libraries/kitchen_helper.rb +13 -0
  49. data/shared/snippet/test_kitchen/templates/default/_kitchen_yml.erb +30 -0
  50. data/shared/snippet/test_kitchen/templates/default/test_integration_default_serverspec_recipes_default_spec_rb.erb +16 -0
  51. data/shared/snippet/test_kitchen/templates/default/test_integration_default_serverspec_spec_helper_rb.erb +7 -0
  52. metadata +372 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8de1c97585788c79d1613949960f551e08333be8
4
+ data.tar.gz: b6192add19f7a575a67f9a4304a5247b82eae21b
5
+ SHA512:
6
+ metadata.gz: f816f07eef02675086c44b518f784a5df10ef4a6e60cc92acc9fd0e013ff312af5d3fee8d46be1b622eb4f30403be6d8df27e8dd8cbc54d10f297bab8d8e0ee9
7
+ data.tar.gz: d5a5b0dee4f4bc59af10859e3fc37772e44a62a7e987f5fac527cd09fe476b327bd50bdcad8feb5f1774dccb8c698df2b31b900e0f1bc9db04e52d62cb465ac1
data/History.md ADDED
@@ -0,0 +1,6 @@
1
+ # Changelog for chef-gen-flavor-base
2
+
3
+ ## 0.9.0
4
+
5
+ * extracted ChefGen::FlavorBase and the snippets in the namespace ChefGen::Snippets into a seperate gem
6
+ * change the mechanism for composing snippets into a flavor from Ruby module inclusion to a declarative method. This allows flavor authors to control the order that snippets are applied. Previously, snippet order was whatever random based on the return of `Object#public_methods`
data/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ # THE APACHE LICENSE, v2
2
+
3
+ Copyright 2015 Nordstrom, Inc.
4
+
5
+ Copyright 2015 James FitzGibbon
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not
8
+ use this file except in compliance with the License. You may obtain a copy
9
+ of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
+ License for the specific language governing permissions and limitations
17
+ under the License.
data/Manifest.txt ADDED
@@ -0,0 +1,50 @@
1
+ History.md
2
+ LICENSE
3
+ Manifest.txt
4
+ README.md
5
+ chef-gen-flavor-base.gemspec
6
+ lib/chef_gen/flavor_base.rb
7
+ lib/chef_gen/flavor_base/copy_helpers.rb
8
+ lib/chef_gen/flavor_base/resource_helpers.rb
9
+ lib/chef_gen/snippet/actions_taken.rb
10
+ lib/chef_gen/snippet/attributes.rb
11
+ lib/chef_gen/snippet/chef_spec.rb
12
+ lib/chef_gen/snippet/cookbook_base.rb
13
+ lib/chef_gen/snippet/debugging.rb
14
+ lib/chef_gen/snippet/example_file.rb
15
+ lib/chef_gen/snippet/example_template.rb
16
+ lib/chef_gen/snippet/git_init.rb
17
+ lib/chef_gen/snippet/guard.rb
18
+ lib/chef_gen/snippet/next_steps.rb
19
+ lib/chef_gen/snippet/no_clobber.rb
20
+ lib/chef_gen/snippet/recipes.rb
21
+ lib/chef_gen/snippet/resource_provider.rb
22
+ lib/chef_gen/snippet/standard_ignore.rb
23
+ lib/chef_gen/snippet/style_foodcritic.rb
24
+ lib/chef_gen/snippet/style_rubocop.rb
25
+ lib/chef_gen/snippet/style_tailor.rb
26
+ lib/chef_gen/snippet/test_kitchen.rb
27
+ lib/chef_gen/snippet_base.rb
28
+ lib/chef_gen/snippets.rb
29
+ shared/snippet/attributes/templates/default/attributes_default_rb.erb
30
+ shared/snippet/chef_spec/templates/default/_rspec.erb
31
+ shared/snippet/chef_spec/templates/default/spec_chef_runner_context_rb.erb
32
+ shared/snippet/chef_spec/templates/default/spec_recipes_default_spec_rb.erb
33
+ shared/snippet/chef_spec/templates/default/spec_spec_helper_rb.erb
34
+ shared/snippet/cookbook_base/templates/default/Berksfile.erb
35
+ shared/snippet/cookbook_base/templates/default/CHANGELOG_md.erb
36
+ shared/snippet/cookbook_base/templates/default/Gemfile.erb
37
+ shared/snippet/cookbook_base/templates/default/README_md.erb
38
+ shared/snippet/cookbook_base/templates/default/Rakefile.erb
39
+ shared/snippet/cookbook_base/templates/default/metadata_rb.erb
40
+ shared/snippet/example_file/files/default/files_default_example_conf
41
+ shared/snippet/example_template/files/default/templates_default_example_conf_erb
42
+ shared/snippet/guard/templates/default/Guardfile.erb
43
+ shared/snippet/recipes/templates/default/recipes_default_rb.erb
44
+ shared/snippet/resource_provider/templates/default/providers_default_rb.erb
45
+ shared/snippet/resource_provider/templates/default/resources_default_rb.erb
46
+ shared/snippet/style_rubocop/templates/default/_rubocop_yml.erb
47
+ shared/snippet/test_kitchen/libraries/kitchen_helper.rb
48
+ shared/snippet/test_kitchen/templates/default/_kitchen_yml.erb
49
+ shared/snippet/test_kitchen/templates/default/test_integration_default_serverspec_recipes_default_spec_rb.erb
50
+ shared/snippet/test_kitchen/templates/default/test_integration_default_serverspec_spec_helper_rb.erb
data/README.md ADDED
@@ -0,0 +1,818 @@
1
+ # chef-gen-flavor-base
2
+
3
+ * home :: https://github.com/jf647/chef-gen-flavor-base
4
+ * license :: [Apache2](http://www.apache.org/licenses/LICENSE-2.0)
5
+ * gem version :: [![Gem Version](https://badge.fury.io/rb/chef-gen-flavor-base.png)](http://badge.fury.io/rb/chef-gen-flavor-base)
6
+ * build status :: [![Circle CI](https://circleci.com/gh/jf647/chef-gen-flavor-base.svg?style=svg)](https://circleci.com/gh/jf647/chef-gen-flavor-base)
7
+ * code climate :: [![Code Climate](https://codeclimate.com/github/jf647/chef-gen-flavor-base/badges/gpa.svg)](https://codeclimate.com/github/jf647/chef-gen-flavor-base)
8
+ * docs :: [![Inline docs](http://inch-ci.org/github/jf647/chef-gen-flavor-base.svg?branch=master)](http://inch-ci.org/github/jf647/chef-gen-flavor-base)
9
+
10
+ ## DESCRIPTION
11
+
12
+ chef-gen-flavor-base is a base class to make it easy to create 'flavors'
13
+ for use with
14
+ [chef-gen-flavors](https://github.com/jf647/chef-gen-flavors).
15
+
16
+ chef-gen-flavors plugs into the 'chef generate' command provided by
17
+ ChefDK to let you provide an alternate template for cookbooks and other
18
+ chef components.
19
+
20
+ This gem simply provides a class your flavor can derive from; templates
21
+ are provided by separate gems, which you can host privately for use
22
+ within your organization or publicly for the Chef community to use.
23
+
24
+ An example flavor that demonstrates how to use this gem is distributed
25
+ separately:
26
+ [chef-gen-flavor-example](https://github.com/jf647/chef-gen-flavor-example)
27
+
28
+ At present this is focused primarily on providing templates for
29
+ generation of cookbooks, as this is where most organization-specific
30
+ customization takes place. Support for the other artifacts that ChefDK
31
+ can generate may work, but is not the focus of early releases.
32
+
33
+ ## INSTALLATION
34
+
35
+ You should not install this gem directly; it will be installed as a
36
+ dependency of a flavor gem. If you are developing a flavor, declare
37
+ this gem a dependency of your gem in your gemspec file:
38
+
39
+ ```ruby
40
+ Gem::Specification.new do |s|
41
+ s.name = 'chef-gen-flavor-awesome'
42
+ s.add_runtime_dependency('chef-gen-flavor-base', ['~> 0.9'])
43
+ ...
44
+ end
45
+ ```
46
+
47
+ Then make bundler read your gemspec:
48
+
49
+ ```ruby
50
+ source 'https://rubygems.org/'
51
+ gemspec
52
+ ```
53
+
54
+ And use bundler to install your dependencies:
55
+
56
+ ```bash
57
+ $ bundle
58
+ ```
59
+
60
+ ## ANATOMY OF A FLAVOR
61
+
62
+ First, some terminology:
63
+
64
+ * a `flavor` is a class that chef-gen-flavors can use to create a directory suitable for passing as the value of `chefdk.generator_cookbook`.
65
+ * `setup mode` is the life of a flavor object constructed by chef-gen-flavors
66
+ * `generate mode` is the life of a flavor object constructed by ChefDK
67
+ * a `snippet` is a class that augments a flavor by defining hooks into the setup and generate workflow. Snippets tend to have a specific purpose, like adding support for Test Kitchen or Rubocop to a cookbook template.
68
+ * setup hooks are called during setup mode to add files (typically templates) to a temporary directory set up by chef-gen-flavors
69
+ * resource hooks are called during generate mode to declare chef resources, typically of type `directory`, `cookbook_file` and `template`.
70
+
71
+ Flavors and Snippets are typically distributed as Rubygems. Because
72
+ snippets are useful to more than one flavor, they are usually
73
+ distributed separately. This gem contains many generic snippets that
74
+ you can use to start creating a flavor to suit your needs.
75
+
76
+ ## A CHEF GENERATE RUN
77
+
78
+ A flavor is constructed twice when you run `chef generate`:
79
+
80
+ First, chef-gen-flavor-base constructs the object, passing a temporary
81
+ directory where files can be copied to. This is called setup mode.
82
+ chef-gen-flavors then calls `#add_content` in the flavor, which
83
+ constructs each snippet. Along the way, various setup hooks are called
84
+ that lets flavors and snippets modify the files copied into the
85
+ temporary directory.
86
+
87
+ Then, chef-dk constructs the object (or more accurately, your generator
88
+ recipe does), passing the type of chef artifact being created (e.g.
89
+ 'cookbook') and a reference to the recipe that resources should be added
90
+ to. This is called generate mode. The generator recipe then calls
91
+ `#declare_resources`, which constructs each snippet. Like setup mode,
92
+ various generate hooks are called to let flavors and snippets modify the
93
+ resources that are used to generate your cookbook
94
+
95
+ ## DIRECTORY LAYOUT
96
+
97
+ This example defines a flavor named `Example`. The full source for this
98
+ example is [available on
99
+ rubygems](https://rubygems.org/gems/chef-gen-flavor-example).
100
+
101
+ The directory structure of a flavor looks like this:
102
+
103
+ ```
104
+ chef-gen-flavor-example
105
+ ├── chef-gen-flavor-example.gemspec
106
+ ├── lib
107
+ │   └── chef_gen
108
+ │   └── flavor
109
+ │   └── example.rb
110
+ └── shared
111
+    └── flavor
112
+    └── example
113
+    ├── metadata.rb
114
+    └── recipes
115
+    └── cookbook.rb
116
+ ```
117
+
118
+ (we are intentionally skipping over some of the ancillary files like
119
+ Rakefile, Gemfile, and unit tests)
120
+
121
+ ## FLAVOR FILES
122
+
123
+ The file `lib/chef_gen/flavor/example.rb` contains the definition of the
124
+ 'example' flavor. It requires the base flavor and the core snippets,
125
+ and inherits from FlavorBase:
126
+
127
+ ```ruby
128
+ require 'chef_gen/flavor_base'
129
+ require 'chef_gen/snippets'
130
+
131
+ module ChefGen
132
+ module Flavor
133
+ class Example < ChefGen::FlavorBase
134
+ NAME = 'example'
135
+ ...
136
+ end
137
+ end
138
+ end
139
+ ```
140
+
141
+ In the constructor, it declares which snippets to mix in, then calls
142
+ initialize in the base class. Order is important here - the base class
143
+ constructs the snippets, so if you call it first, nothing gets
144
+ initialized.
145
+
146
+ ```ruby
147
+ def initialize(temp_path: nil, target_path: nil, type: nil, recipe: nil)
148
+ super
149
+ @snippets << ChefGen::Snippet::CookbookBase
150
+ @snippets << ChefGen::Snippet::Attributes
151
+ ...
152
+ end
153
+ ```
154
+
155
+ The flavor also defines the do_add_content hook to copy files into the temporary
156
+ directory that chef-gen-flavors will pass to ChefDK:
157
+
158
+ ```ruby
159
+ do_add_content do
160
+ @tocopy << [File.expand_path(File.join(static_content_path(__FILE__))) + '/.']
161
+ end
162
+ ```
163
+
164
+ `#add_content` is defined in FlavorBase, and is the mechanism by which
165
+ the contents hooks in the snippets get called, so it is important to
166
+ also call the method in the base class.
167
+
168
+ `#static_content_path` is a helper method that returns the standard
169
+ path to static content, which is
170
+ `$FILE/../../../shared/flavor/$FLAVORNAME`. For example, when called
171
+ as in the example above (which is in `lib/chef_gen/flavor/example.rb`),
172
+ the path returned is `shared/flavor/example`.
173
+
174
+ For a simple flavor that just uses the shared snippets, the static
175
+ content will typically just be a `metadata.rb` file and the generator
176
+ recipes (`recipes/cookbook.rb` in our example). It is important that
177
+ the cookbook name in metadata.rb match the name of the flavor, or
178
+ ChefDK will not be able to find the cookbook:
179
+
180
+ ```ruby
181
+ name 'example'
182
+ version '0.0.0'
183
+ ```
184
+
185
+ The generator recipes typically just construct an object and call
186
+ `#declare_resources` on it:
187
+
188
+ ```ruby
189
+ ChefGen::Flavor::Awesome.new(
190
+ type: 'cookbook', recipe: self
191
+ ).declare_resources
192
+ ```
193
+
194
+ `#declare_resources` is defined in FlavorBase, and is the mechanism by
195
+ which the contents hooks in the snippets get called, so it is important
196
+ to also call the method in the base class. A flavor can also declare
197
+ resources at generate time:
198
+
199
+ ```ruby
200
+ do_declare_resources do
201
+ @recipe.send(:directory, 'test')
202
+ end
203
+ ```
204
+
205
+ It is more common for resources to be declared via snippets though.
206
+
207
+ ## SIMPLE RESOURCES
208
+
209
+ FlavorBase provides five accessors for adding simple resources to a
210
+ recipe when running in generate mode:
211
+
212
+ * directories
213
+ * files
214
+ * files_if_missing
215
+ * templates
216
+ * templates_if_missing
217
+
218
+ Each element of these lists is a path to the target in the generated
219
+ cookbook, so to set up a template to create a recipes/default.rb file,
220
+ just push that value onto the @templates_if_missing list:
221
+
222
+ ```ruby
223
+ do_declare_resourced do
224
+ templates_if_missing << File.join('recipes', 'default.rb')
225
+ end
226
+ ```
227
+
228
+ The source of files and directories is a mangled form of the
229
+ destination, with slashes and dots replaced by underscores. Templates
230
+ additionally have `.erb` appended to the source. The template resource
231
+ set up by the above declaration looks like this:
232
+
233
+ ```ruby
234
+ template "#{dest_path}/recipes/default.rb" do
235
+ source 'recipes_default_rb.erb'
236
+ helpers ChefDK::Generator::TemplateHelper
237
+ end
238
+ ```
239
+
240
+ If you need to construct more complex files and templates, you can call
241
+ `#add_files` or `#add_templates` directly; look at the CookbookBase
242
+ snippet for examples of how this is done.
243
+
244
+ ## FLAVOR HOOKS
245
+
246
+ Flavors have two main entry points: `#add_content`, called during setup
247
+ mode and `#declare_resources`, called during generate mode. In each mode,
248
+ there are various hook points to allow flavors and snippets to modify what
249
+ happens.
250
+
251
+ Hooks are called in the context of the flavor object. Some hooks are
252
+ passed arguments.
253
+
254
+ In practice, the `do_add_content` and `do_declare_resources` hooks are
255
+ where most customization takes place.
256
+
257
+ Note that when declaring a hook directly in a flavor, you just add the
258
+ block as part of the class definition:
259
+
260
+ ```ruby
261
+ module ChefGen
262
+ module Flavor
263
+ class AwesomeOverride1 < ChefGen::Flavor::Awesome
264
+ NAME = 'awesome_override1'
265
+
266
+ before_copy_content do
267
+ @tocopy << [File.expand_path(File.join(static_content_path(__FILE__))) + '/.']
268
+ end
269
+ end
270
+ end
271
+ end
272
+ ```
273
+
274
+ To get the same effect in a snippet, you have to add the hook to the
275
+ flavor's class using one of the initializers:
276
+
277
+ ```ruby
278
+ module ChefGen
279
+ module Snippet
280
+ class BeforeCopyContent < ChefGen::SnippetBase
281
+ NAME = 'before_copy_content'
282
+
283
+ private
284
+
285
+ def initialize_generate
286
+ @flavor.class.before_copy_content do
287
+ @tocopy << [File.expand_path(File.join(static_content_path(__FILE__))) + '/.']
288
+ end
289
+ end
290
+ end
291
+ end
292
+ end
293
+ ```
294
+
295
+ ### COMMON HOOKS
296
+
297
+ These hooks are called in both setup and generate mode:
298
+
299
+ #### before_initialize
300
+
301
+ Called before any flavor initialization has taken place in
302
+ ChefGen::FlavorBase.
303
+
304
+ #### after_initialize
305
+
306
+ Called after flavor initialization has taken place in
307
+ ChefGen::FlavorBase.
308
+
309
+ #### before_snippet_construct
310
+
311
+ Called before snippets are constructed.
312
+
313
+ #### after_snippet_construct
314
+
315
+ Called afetr snippets have been constructed and marked as active.
316
+
317
+ ### SETUP HOOKS
318
+
319
+ These hooks are called in setup mode when `#add_content` is called.
320
+
321
+ #### before_add_content
322
+
323
+ Called immediately before the do_add_content hooks are invoked.
324
+
325
+ #### do_add_content
326
+
327
+ Called to add content to the temporary directory. Typically this is
328
+ used to copy static files (templates, etc.) from a flavor or snippet.
329
+
330
+ #### after_add_content
331
+
332
+ Called immediately after the do_add_content hooks are invoked.
333
+
334
+ #### before_copy_content
335
+
336
+ Called immediately before `#copy_content` is invoked to copy files to the
337
+ temporary directory.
338
+
339
+ #### after_copy_content
340
+
341
+ Called immediately after `#copy_content` is invoked. Typically this is
342
+ used to replace a file that comes from a parent flavor or another
343
+ snippet, ensuring that the replacement gets copied over the other file.
344
+
345
+ ### GENERATE HOOKS
346
+
347
+ These hooks are called in generate mode when `#declare_resources` is
348
+ called.
349
+
350
+ #### before_declare_resources
351
+
352
+ Called immediately before the do_declare_resources are invoked.
353
+
354
+ #### do_declare_resources
355
+
356
+ Called to add resources to the recipe. Typically this is used to
357
+ manipulate the @directories, @files and @templates lists, but more
358
+ complex use cases are possible.
359
+
360
+ #### after_declare_resources
361
+
362
+ Called immediately after the do_declare_resources are invoked.
363
+
364
+ #### before_add_resources
365
+
366
+ Called immediately before `#add_resources` adds the entries in the
367
+ @directories, @files, and @templates lists to the recipe.
368
+
369
+ #### after_add_resources
370
+
371
+ Called immediately after `#add_resources` adds the entries in the
372
+ @directories, @files, and @templates lists to the recipe.
373
+
374
+ #### before_add_directories(directories)
375
+
376
+ Called before `#add_resources` adds directories to the recipe. Passed a
377
+ list of directories that will be added.
378
+
379
+ #### after_add_directories(directories)
380
+
381
+ Called after `#add_resources` adds directories to the recipe. Passed a
382
+ list of directories that will be added. Can be used for reporting
383
+ purposes (see the ActionsTaken snippet for an example).
384
+
385
+ #### before_add_files(files, resource_action, type, attrs)
386
+
387
+ Called before `#add_resources` adds files to the recipe. Passed a list
388
+ of files, the action to give the resource, the resource type (:file or
389
+ :cookbook_file) and a hash of additional attributes. Can be used to
390
+ implement protection schemes (see the NoClobber snippet for an example).
391
+
392
+ #### after_add_files(files, resource_action, type, attrs)
393
+
394
+ Called after `#add_resources` adds files to the recipe. Passed a list
395
+ of files, the action to give the resource, the resource type (:file or
396
+ :cookbook_file) and a hash of additional attributes. Can be used for
397
+ reporting purposes (see the ActionsTaken snippet for an example).
398
+
399
+ #### before_add_templates(templates, resource_action, attrs)
400
+
401
+ Called before `#add_resources` adds templates to the recipe. Passed a
402
+ list of files, the action to give the resource and a hash of additional
403
+ attributes. Can be used to implement protection schemes (see the
404
+ NoClobber snippet for an example).
405
+
406
+ #### after_add_templates(templates, resource_action, attrs)
407
+
408
+ Called after `#add_resources` adds templates to the recipe. Passed a
409
+ list of files, the action to give the resource and a hash of additional
410
+ attributes. Can be used for reporting purposes (see the ActionsTaken
411
+ snippet for an example).
412
+
413
+ ## SNIPPET FILES
414
+
415
+ A snippet is a class that derives from `ChefGen::SnippetBase`:
416
+
417
+ ```ruby
418
+ require 'chef_gen/snippet_base'
419
+
420
+ module ChefGen
421
+ module Snippet
422
+ class Attributes < ChefGen::SnippetBase
423
+ NAME = 'attributes'
424
+ ...
425
+ end
426
+ end
427
+ end
428
+ ```
429
+
430
+ A snippet typically alters a flavor in one of three ways: by adding
431
+ accessors, such as a list of gems to write to a Gemfile:
432
+
433
+ The snippet base class provides a constructor that invokes
434
+ `#initialize_setup` in setup mode and `#intialize_generate` in generate
435
+ mode.
436
+
437
+ Snippets can add accessors to the flavor:
438
+
439
+ ```ruby
440
+ def initialize_generate
441
+ @flavor.class.send(:attr_accessor, :cookbook_gems)
442
+ @flavor.cookbook_gems = { 'rake' => '~> 10.4' }
443
+ end
444
+ ```
445
+
446
+ Or add a hook to copy content to the temporary directory. Like a
447
+ flavor, `ChefGen::SnippetBase` provides a `#static_content_path` method
448
+ that finds the standard location of files packaged with the snippet,
449
+ using the name.
450
+
451
+ ```ruby
452
+ def initialize_setup
453
+ snippet_content_path = File.expand_path(File.join(static_content_path(__FILE__))) + '/.'
454
+ @flavor.class.do_add_content do
455
+ tocopy << [snippet_content_path]
456
+ end
457
+ end
458
+ ```
459
+
460
+ (Note how the static content path is calculated outside the hook block,
461
+ to ensure that we use `#static_content_path` defined in SnippetBase. If
462
+ it were inside the hook block, which executes in the scope of the flavor
463
+ object, then `#static_content_path` in FlavorBase would be used.)
464
+
465
+ A snippet might also add a hook to declare resources at generate time:
466
+
467
+ ```ruby
468
+ def initialize_generate
469
+ @flavor.class.do_declare_resources do
470
+ directories << 'spec'
471
+ directories << File.join('spec', 'recipes')
472
+ templates << '.rspec'
473
+ templates_if_missing << File.join('spec', 'spec_helper.rb')
474
+ templates_if_missing << File.join('spec', 'recipes', 'default_spec.rb')
475
+ templates_if_missing << File.join('spec', 'chef_runner_context.rb')
476
+ end
477
+ end
478
+ ```
479
+
480
+ ## BUILT-IN SNIPPETS
481
+
482
+ chef-gen-flavor-base comes with snippets that can be mixed and matched
483
+ to create a complete cookbook skeleton. Also take a look at the
484
+ [example flavor](https://github.com/jf647/chef-gen-flavor-example) to
485
+ see how to use them.
486
+
487
+ For full documentation, run `bundle exec rake doc` in the source repo
488
+ then open `doc/index.html`.
489
+
490
+ ### CookbookBase
491
+
492
+ * declares and provides a template for the basic files that any cookbook needs:
493
+ * Gemfile
494
+ * Berksfile
495
+ * Rakefile
496
+ * metadata.rb
497
+ * README.md
498
+ * CHANGELOG.md
499
+ * provides an accessor `#cookbook_gems` which is a hash with gem names as keys and constraints as values, defaulting to rake and berkshelf
500
+ * provides an accessor `#gem_sources`, which is a list preloaded with https://rubygems.org
501
+ * provides an accessor `#berks_sources`, which is a list preloaded with https://supermarket.chef.io
502
+ * provides an accessor `#rake_tasks`, which is a hash with task names as keys and full task definitions as values
503
+
504
+ ### ActionsTaken
505
+
506
+ * provides an accessor `#actions_taken`, which is a list of strings representing what actions the flavor took
507
+ * adds before hooks to `#add_files`, `#add_templates` and `#add_directories` to add those actons to the actions_taken list
508
+ * adds an after hook to `#declare_resources` that adds a ruby_block to the recipe to report on what actions the flavor took
509
+
510
+ Any resources that you add to a flavor manually (not using the add_*
511
+ helpers) should be added to the actions_taken list manually:
512
+
513
+ ```ruby
514
+ def initialize_generate
515
+ @recipe.send(:execute, 'initialize git repo') do
516
+ command('git init .')
517
+ end
518
+ actions_taken << 'initialize git repo' if snippet?('actions_taken')
519
+ ```
520
+
521
+ ### Attributes
522
+
523
+ * declares and provides a template for attributes/default.rb
524
+
525
+ ### ChefSpec
526
+
527
+ * declares and provides a template for ChefSpec files:
528
+ * .rspec
529
+ * spec/spec_helper.rb
530
+ * spec/chef_run_context.rb
531
+ * spec/recipes/default_spec.rb
532
+ * adds the ChefSpec gems
533
+ * adds the ChefSpec rake tasks
534
+ * adds the ChefSpec guard set
535
+
536
+ ### Debugging
537
+
538
+ * adds the pry, pry-byebug and pry-stack_explorer gems, to make debugging cookbooks easier
539
+
540
+ ### ExampleFile
541
+
542
+ * declares and provides a cookbook_file for files/default/example.conf
543
+
544
+ ### ExampleTemplate
545
+
546
+ * declares and provides a cookbook_file for templates/default/example.conf.erb
547
+
548
+ ### GitInit
549
+
550
+ * executes 'git init .' after generation if git is installed and the `--skip-git-init` switch has not been passed
551
+
552
+ ### Guard
553
+
554
+ * provides an accessor `#guard_sets`, which is a hash with set names as keys and full set definitions as values
555
+
556
+ ### NextSteps
557
+
558
+ * provides an accessor `#next_steps`, which can be used to display some guidance to the user on how to proceed
559
+
560
+ To set next steps in a recipe, define it in your generate recipe like so:
561
+
562
+ ```ruby
563
+ f = ChefGen::Flavor::Awesome.new(
564
+ type: 'cookbook', recipe: self
565
+ )
566
+ f.class.after_declare_resources do
567
+ self.next_steps = <<END
568
+
569
+ go forth and conquer
570
+
571
+ END
572
+ end
573
+ f.declare_resources
574
+ ```
575
+
576
+ ### NoClobber
577
+
578
+ * provides an accessor `#fail_on_clobber`, which is a boolean. If true, then attempts to add files or templates with an action of :create (i.e. not @files_if_missing or @templates_if_missing) will cause the generate to fail. To allow for files to be clobbered, users need to pass `-a clobber` to `chef generate`
579
+
580
+ ### Recipes
581
+
582
+ * declares and provides a cookbook_file for recipes/default.rb
583
+
584
+ ### ResourceProvider
585
+
586
+ * declares and provides a cookbook_file for:
587
+ * resources/default.rb
588
+ * providers/default.rb
589
+
590
+ ### StandardIgnore
591
+
592
+ * declares the two ignore files used by chef cookbook repos:
593
+ * chefignore
594
+ * .gitignore
595
+ * provides two accessors: `#chefignore_patterns` and `#gitignore_patterns`, both of which are lists of patterns to write to `chefignore` and `.gitignore` respectively
596
+ * preloads the standard ignore patterns for chefignore and .gitignore
597
+
598
+ ### StyleFoodcritic
599
+
600
+ * adds the Foodcritic gems
601
+ * adds the Foodcritic rake task
602
+ * adds the Foodcritic guard set
603
+
604
+ ### StyleRubocop
605
+
606
+ * declares and provides a template for .rubocop.yml
607
+ * adds the Rubocop gems
608
+ * adds the Rubocop rake task
609
+ * adds the Rubocop guard set
610
+
611
+ ### StyleTailor
612
+
613
+ * adds the Tailor gems
614
+ * adds the Tailor rake task
615
+
616
+ ### TestKitchen
617
+
618
+ * declares and provides a template for ChefSpec files:
619
+ * .kitchen.yml
620
+ * test/integration/default/serverspec/spec_helper.rb
621
+ * test/integration/default/serverspec/recipes/default_spec.rb
622
+ * adds the Test Kitchen gems
623
+ * adds the Test Kitchen rake tasks
624
+ * adds the Test Kitchen guard set
625
+
626
+ ## SNIPPET DEPENDENCIES
627
+
628
+ Sometimes snippets need to depend on features provided by another
629
+ snippet. For example, the CookbookBase snippet will add patterns to the
630
+ .gitignore and chefignore files, but only if the StandardIgnore snippet
631
+ has also been included in the flavor. A common pattern is to test
632
+ whether a snippet has been enabled during generation like so:
633
+
634
+ ```ruby
635
+ def initialize_generate
636
+ @flavor.class.do_declare_resources do
637
+ chefignore_patterns << '.rubocop.yml' if snippet?('standard_ignore')
638
+ end
639
+ end
640
+ ```
641
+
642
+ ## EXAMPLE OVERRIDES
643
+
644
+ Because flavors and snippets are just ruby classes, you can override
645
+ them using normal object techniques. It is important to call the
646
+ superclass method in FlavorBase or SnippetBase, as this is where the
647
+ important work tends to happen, but you have some flexibility around
648
+ whether you call it before or after the code in your flavor or snippet.
649
+
650
+ ### Deriving one flavor from another
651
+
652
+ Rather than inheriting from ChefGen::FlavorBase, your flavor can inherit
653
+ from another flavor that inherits from ChefGen::FlavorBase. In this way
654
+ you get all of the declarations and content of your parent flavor, plus
655
+ have the ability to tweak and change one or two little things that don't
656
+ fit your environment.
657
+
658
+ ```ruby
659
+ module ChefGen
660
+ module Flavor
661
+ class MoarAwesome < ChefGen::Flavor::Awesome
662
+ NAME = 'moar_awesome'
663
+ end
664
+ end
665
+ end
666
+ ```
667
+
668
+ ### Removing a snippet from the parent flavor
669
+
670
+ Perhaps a flavor does everything you want, but there's one thing you
671
+ don't like about it. You can remove that snippet from the list of
672
+ snippets by hooking after_initialize:
673
+
674
+ ```ruby
675
+ module ChefGen
676
+ module Flavor
677
+ class LessAwesome < ChefGen::Flavor::Awesome
678
+ NAME = 'less_awesome'
679
+
680
+ after_initialize do
681
+ @snippets.reject! do |e|
682
+ e == ChefGen::Snippet::ExampleFile || e == ChefGen::Snippet::ExampleTemplate
683
+ end
684
+ end
685
+ end
686
+ end
687
+ end
688
+ ```
689
+
690
+ ### Changing a template file in the parent flavor
691
+
692
+ If you want to keep a declaration but change the source file used for a
693
+ flavor, you can hook before_copy_content (or after_add_content):
694
+
695
+ ```ruby
696
+ module ChefGen
697
+ module Flavor
698
+ class AlmostPerfect < ChefGen::Flavor::Awesome
699
+ NAME = 'almost_perfect'
700
+
701
+ before_copy_content do
702
+ @tocopy << [File.expand_path(File.join(static_content_path(__FILE__))) + '/.']
703
+ end
704
+ end
705
+ end
706
+ end
707
+ ```
708
+
709
+ This example assumes that the awesome flavor includes the TestKitchen
710
+ snippet (which declares the serverspec spec_helper.rb file) and that you
711
+ package an alternate template with your flavor in
712
+ `shared/flavor/almost_perfect/templates/default/test_integration_default_serverspec_spec_helper_rb.erb`
713
+
714
+ ### Adding a Rake task
715
+
716
+ To add an additional Rake task to a flavor:
717
+
718
+ ```ruby
719
+ module ChefGen
720
+ module Flavor
721
+ class NeedsATask < ChefGen::Flavor::Awesome
722
+ NAME = 'needs_a_task'
723
+
724
+ do_declare_resources do
725
+ rake_tasks['foo'] = <<'END'
726
+ task :foo do
727
+ sh 'echo foo'
728
+ end
729
+ END
730
+ end
731
+ end
732
+ end
733
+ end
734
+
735
+ ```
736
+
737
+ ### Removing a resource using a snippet
738
+
739
+ Here, we've used a snippet to remove resources from an snippet that
740
+ was run earlier:
741
+
742
+ ```ruby
743
+ require 'chef_gen/flavor_base'
744
+ require 'chef_gen/snippet_base'
745
+
746
+ module ChefGen
747
+ module Snippet
748
+ class RemoveExamples < ChefGen::SnippetBase
749
+ NAME = 'remove_examples'
750
+
751
+ private
752
+
753
+ def initialize_generate
754
+ @flavor.class.after_declare_resources do
755
+ directories.reject! do |e|
756
+ %w(files files/default templates templates/default).include?(e)
757
+ end
758
+ files_if_missing.reject! do |e|
759
+ %w(files/default/example.conf templates/default/example.conf.erb).include?(e)
760
+ end
761
+ end
762
+ end
763
+ end
764
+ end
765
+ end
766
+
767
+ module ChefGen
768
+ module Flavor
769
+ class DoNotLikeExamples < ChefGen::Flavor::Awesome
770
+ NAME = 'do_not_like_examples'
771
+
772
+ def initialize(temp_path: nil, type: nil, recipe: nil)
773
+ super
774
+ @snippets << ChefGen::Snippet::RemoveExamples
775
+ end
776
+ end
777
+ end
778
+ end
779
+ ```
780
+
781
+ Note that when we manipulate the resource lists in a snippet, we have
782
+ to use `@flavor.resource_type`, not just `@resource_type`, as the
783
+ resource lists are members of the flavor, not the snippet.
784
+
785
+ ## TESTING A FLAVOR
786
+
787
+ chef-gen-flavors provides a number of useful step definitions for Aruba
788
+ (a CLI driver for Cucumber) to make it easier to test flavors. To access
789
+ these definitions, add the following line to your
790
+ `features/support/env.rb` file:
791
+
792
+ require 'chef_gen/flavors/cucumber'
793
+
794
+ For an example of how to use these steps in your features, refer to the
795
+ features in the `features` directory and the fixtures in
796
+ `spec/support/fixtues`.
797
+
798
+ ## AUTHOR
799
+
800
+ [James FitzGibbon](https://github.com/jf647)
801
+
802
+ ## LICENSE
803
+
804
+ Copyright 2015 Nordstrom, Inc.
805
+
806
+ Copyright 2015 James FitzGibbon
807
+
808
+ Licensed under the Apache License, Version 2.0 (the "License"); you may
809
+ not use this file except in compliance with the License. You may obtain
810
+ a copy of the License at
811
+
812
+ http://www.apache.org/licenses/LICENSE-2.0
813
+
814
+ Unless required by applicable law or agreed to in writing, software
815
+ distributed under the License is distributed on an "AS IS" BASIS,
816
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
817
+ See the License for the specific language governing permissions and
818
+ limitations under the License.