chef-gen-flavor-base 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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.