From 87d1f8377fdd7a2345f52e95c1b7075c25104cbe Mon Sep 17 00:00:00 2001 From: Adam Jacob Date: Mon, 28 Jul 2014 15:49:20 -0700 Subject: [PATCH] Make it easier to write new generators Adds ChefDK::Generator.add_attr_to_context, which handles adding instance variables (and optionally their values) to the ChefDK::Generator::Context and ChefDK::Generator::TemplateHelper. This makes writing new generators much easier, because you don't have to be aware of how/when/where the context variables are set up - you can just focus in on the generator you are adding. Additionally, any option added to a generator is dynamically added to the Context and TemplateHelper if it has a value, making the barrier even smaller. One thing to note is that generator options really should either be true/false or have default values that make sense for the generator, or you're liable to get errors. (This is appropriate, I think) While cleaning up the existing context accessors, I noticed that at least one of them (license_description) existed in a template, but was never set anywhere. I implemented the method within the TemplateHelper, so that you can dynamically ask for the correct license description to embedd in a file, including what to comment the license with. --- lib/chef-dk/command/generator_commands.rb | 5 +- lib/chef-dk/command/generator_commands/app.rb | 8 +- .../command/generator_commands/base.rb | 17 +-- .../command/generator_commands/cookbook.rb | 6 +- .../generator_commands/cookbook_code_file.rb | 6 +- .../generator_commands/cookbook_file.rb | 2 +- .../command/generator_commands/repo.rb | 6 +- .../command/generator_commands/template.rb | 3 +- lib/chef-dk/generator.rb | 135 +++++++++++++----- .../templates/default/default_recipe.rb.erb | 5 +- .../templates/default/metadata.rb.erb | 6 +- spec/unit/generator_spec.rb | 120 ++++++++++++++++ 12 files changed, 246 insertions(+), 73 deletions(-) create mode 100644 spec/unit/generator_spec.rb diff --git a/lib/chef-dk/command/generator_commands.rb b/lib/chef-dk/command/generator_commands.rb index 7abe83e17..c8e1515bc 100644 --- a/lib/chef-dk/command/generator_commands.rb +++ b/lib/chef-dk/command/generator_commands.rb @@ -31,6 +31,8 @@ module Command module SharedGeneratorOptions include Mixlib::CLI + # You really want these to have default values, as + # they will likely be used all over the place. option :license, :short => "-I LICENSE", :long => "--license LICENSE", @@ -46,7 +48,8 @@ module SharedGeneratorOptions option :email, :short => "-m EMAIL", :long => "--email EMAIL", - :description => "Email address of the author" + :description => "Email address of the author - defaults to 'you@example.com'", + :default => 'you@example.com' option :generator_cookbook, :short => "-g GENERATOR_COOKBOOK_PATH", diff --git a/lib/chef-dk/command/generator_commands/app.rb b/lib/chef-dk/command/generator_commands/app.rb index 6a6c77e73..e4e5d7a28 100644 --- a/lib/chef-dk/command/generator_commands/app.rb +++ b/lib/chef-dk/command/generator_commands/app.rb @@ -53,10 +53,10 @@ def run def setup_context super - generator_context.app_root = app_root - generator_context.app_name = app_name - generator_context.cookbook_root ||= cookbook_root - generator_context.cookbook_name ||= cookbook_name + Generator.add_attr_to_context(:app_root, app_root) + Generator.add_attr_to_context(:app_name, app_name) + Generator.add_attr_to_context(:cookbook_root, cookbook_root) + Generator.add_attr_to_context(:cookbook_name, cookbook_name) end def recipe diff --git a/lib/chef-dk/command/generator_commands/base.rb b/lib/chef-dk/command/generator_commands/base.rb index cf0d66871..6af5f0604 100644 --- a/lib/chef-dk/command/generator_commands/base.rb +++ b/lib/chef-dk/command/generator_commands/base.rb @@ -33,7 +33,6 @@ class Base < Command::Base attr_reader :params - options.merge!(SharedGeneratorOptions.options) def initialize(params) @@ -56,17 +55,11 @@ def generator_cookbook_path # Sets git related generator_context values. def setup_context - Generator.context.have_git = have_git? - Generator.context.skip_git_init = false - Generator.context.license = config[:license] - Generator.context.copyright_holder = config[:copyright_holder] - Generator.context.email = config[:email] - end - - # Delegates to `Generator.context`, the singleton instance of - # Generator::Context - def generator_context - Generator.context + Generator.add_attr_to_context(:have_git, have_git?) + Generator.add_attr_to_context(:skip_git_init, false) + config.each do |k,v| + Generator.add_attr_to_context(k, v) + end end # Checks the `PATH` for the presence of a `git` (or `git.exe`, on diff --git a/lib/chef-dk/command/generator_commands/cookbook.rb b/lib/chef-dk/command/generator_commands/cookbook.rb index e87d6917e..90ef11af1 100644 --- a/lib/chef-dk/command/generator_commands/cookbook.rb +++ b/lib/chef-dk/command/generator_commands/cookbook.rb @@ -56,9 +56,9 @@ def run def setup_context super - generator_context.skip_git_init = cookbook_path_in_git_repo? - generator_context.cookbook_root = cookbook_root - generator_context.cookbook_name = cookbook_name + Generator.add_attr_to_context(:skip_git_init, cookbook_path_in_git_repo?) + Generator.add_attr_to_context(:cookbook_root, cookbook_root) + Generator.add_attr_to_context(:cookbook_name, cookbook_name) end def recipe diff --git a/lib/chef-dk/command/generator_commands/cookbook_code_file.rb b/lib/chef-dk/command/generator_commands/cookbook_code_file.rb index 1f5116992..f5b1b183a 100644 --- a/lib/chef-dk/command/generator_commands/cookbook_code_file.rb +++ b/lib/chef-dk/command/generator_commands/cookbook_code_file.rb @@ -55,9 +55,9 @@ def run def setup_context super - generator_context.cookbook_root = cookbook_root - generator_context.cookbook_name = cookbook_name - generator_context.new_file_basename = new_file_basename + Generator.add_attr_to_context(:cookbook_root, cookbook_root) + Generator.add_attr_to_context(:cookbook_name, cookbook_name) + Generator.add_attr_to_context(:new_file_basename, new_file_basename) end def cookbook_root diff --git a/lib/chef-dk/command/generator_commands/cookbook_file.rb b/lib/chef-dk/command/generator_commands/cookbook_file.rb index f58091b6f..4031f3541 100644 --- a/lib/chef-dk/command/generator_commands/cookbook_file.rb +++ b/lib/chef-dk/command/generator_commands/cookbook_file.rb @@ -37,7 +37,7 @@ def recipe def setup_context super - generator_context.content_source = config[:source] + Generator.add_attr_to_context(:content_source, config[:source]) end end end diff --git a/lib/chef-dk/command/generator_commands/repo.rb b/lib/chef-dk/command/generator_commands/repo.rb index 07db525eb..fd62cbd35 100644 --- a/lib/chef-dk/command/generator_commands/repo.rb +++ b/lib/chef-dk/command/generator_commands/repo.rb @@ -59,10 +59,8 @@ def run def setup_context super - generator_context.repo_root = repo_root - generator_context.repo_name = repo_name - generator_context.cli_config = config - generator_context.policy_only = config[:policy_only] + Generator.add_attr_to_context(:repo_root, repo_root) + Generator.add_attr_to_context(:repo_name, repo_name) end def recipe diff --git a/lib/chef-dk/command/generator_commands/template.rb b/lib/chef-dk/command/generator_commands/template.rb index 3c843baf8..b4bf8e09a 100644 --- a/lib/chef-dk/command/generator_commands/template.rb +++ b/lib/chef-dk/command/generator_commands/template.rb @@ -23,7 +23,7 @@ module GeneratorCommands # chef generate template [path/to/cookbook_root] name --source=source_file class Template < CookbookCodeFile - option :source, + option :content_source, :short => "-s SOURCE_FILE", :long => "--source SOURCE_FILE", :description => "Copy content from SOURCE_FILE" @@ -38,7 +38,6 @@ def recipe def setup_context super - generator_context.content_source = config[:source] end end end diff --git a/lib/chef-dk/generator.rb b/lib/chef-dk/generator.rb index 3744451fc..fe460b2c8 100644 --- a/lib/chef-dk/generator.rb +++ b/lib/chef-dk/generator.rb @@ -19,26 +19,11 @@ module ChefDK module Generator + # This is here to hold attr_accessor data for Generator context variables class Context - attr_accessor :have_git - attr_accessor :app_root - attr_accessor :cookbook_root - attr_accessor :app_name - attr_accessor :cookbook_name - attr_accessor :new_file_basename - attr_accessor :content_source - attr_accessor :author_name - attr_accessor :author_email - attr_accessor :license - attr_accessor :license_description - attr_accessor :license_text - attr_accessor :repo_root - attr_accessor :repo_name - attr_accessor :cli_config - attr_accessor :skip_git_init - attr_accessor :copyright_holder - attr_accessor :email - attr_accessor :policy_only + def self.add_attr(name) + attr_accessor(name) + end end def self.reset @@ -49,6 +34,13 @@ def self.context @context ||= Context.new end + def self.add_attr_to_context(name, value=nil) + sym_name = name.to_sym + ChefDK::Generator::Context.add_attr(sym_name) + ChefDK::Generator::TemplateHelper.delegate_to_app_context(sym_name) + context.public_send("#{sym_name}=", value) + end + module TemplateHelper def self.delegate_to_app_context(name) @@ -61,23 +53,94 @@ def year Time.now.year end - # delegate all the attributes of app_config - delegate_to_app_context :app_root - delegate_to_app_context :cookbook_root - delegate_to_app_context :cookbook_name - delegate_to_app_context :new_file_basename - delegate_to_app_context :content_source - delegate_to_app_context :author_name - delegate_to_app_context :author_email - delegate_to_app_context :email - delegate_to_app_context :license - delegate_to_app_context :license_description - delegate_to_app_context :license_text - delegate_to_app_context :repo_root - delegate_to_app_context :repo_name - delegate_to_app_context :copyright_holder - delegate_to_app_context :cli_config - delegate_to_app_context :policy_only + # Prints the short description of the license, suitable for use in a + # preamble to a file. Optionally specify a comment to prepend to each line. + def license_description(comment=nil) + case license + when 'all_rights' + result = "Copyright (c) #{year} #{copyright_holder}, All Rights Reserved." + when 'apache2' + result = <<-EOH +Copyright #{year} #{copyright_holder} + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +EOH + when 'mit' + result = <<-EOH +The MIT License (MIT) + +Copyright (c) #{year} #{copyright_holder} + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +EOH + when 'gplv2' + result = <<-EOH +Copyright (C) #{year} #{copyright_holder} + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +EOH + when 'gplv3' + result = <<-EOH +Copyright (C) #{year} #{copyright_holder} + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +EOH + end + if comment + result.gsub(/^/m, "#{comment} ") + else + result + end + end end end diff --git a/lib/chef-dk/skeletons/code_generator/templates/default/default_recipe.rb.erb b/lib/chef-dk/skeletons/code_generator/templates/default/default_recipe.rb.erb index f199644bc..9e2417303 100644 --- a/lib/chef-dk/skeletons/code_generator/templates/default/default_recipe.rb.erb +++ b/lib/chef-dk/skeletons/code_generator/templates/default/default_recipe.rb.erb @@ -2,7 +2,4 @@ # Cookbook Name:: <%= cookbook_name %> # Recipe:: default # -# Copyright (C) <%= Time.new.strftime("%Y") %> <%= author_name %> -# -# <%= license_description %> -# +<%= license_description('#') %> diff --git a/lib/chef-dk/skeletons/code_generator/templates/default/metadata.rb.erb b/lib/chef-dk/skeletons/code_generator/templates/default/metadata.rb.erb index eda0b48f9..a69f1ef0e 100644 --- a/lib/chef-dk/skeletons/code_generator/templates/default/metadata.rb.erb +++ b/lib/chef-dk/skeletons/code_generator/templates/default/metadata.rb.erb @@ -1,7 +1,7 @@ name '<%= cookbook_name %>' -maintainer '<%= author_name %>' -maintainer_email '<%= author_email %>' -license '<%= license_description %>' +maintainer '<%= copyright_holder %>' +maintainer_email '<%= email %>' +license '<%= license %>' description 'Installs/Configures <%= cookbook_name %>' long_description 'Installs/Configures <%= cookbook_name %>' version '0.1.0' diff --git a/spec/unit/generator_spec.rb b/spec/unit/generator_spec.rb new file mode 100644 index 000000000..1a3ebd4a7 --- /dev/null +++ b/spec/unit/generator_spec.rb @@ -0,0 +1,120 @@ +# +# Copyright:: Copyright (c) 2014 Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' +require 'chef-dk/generator' + +describe ChefDK::Generator do + + before(:each) do + ChefDK::Generator.reset + end + + describe "self.add_attr_to_context" do + it "adds an accessor for the symbol to the context object" do + ChefDK::Generator.add_attr_to_context(:snakes) + expect(ChefDK::Generator.context.snakes = 5).to be_eql(5) + expect(ChefDK::Generator.context.snakes).to be_eql(5) + end + + it "delegates the accessor from the template helper" do + ChefDK::Generator.add_attr_to_context(:snakes) + ChefDK::Generator.context.snakes = 5 + expect(ChefDK::Generator::TemplateHelper.instance_methods).to include(:snakes) + end + + it "sets a value" do + ChefDK::Generator.add_attr_to_context(:snakes, 5) + expect(ChefDK::Generator.context.snakes).to be_eql(5) + end + end +end + +class TmplTest + include ChefDK::Generator::TemplateHelper +end + +describe ChefDK::Generator::TemplateHelper do + let(:license) { 'all_rights' } + let(:copyright_holder) { 'Adam Jacob' } + let(:helper) { TmplTest.new } + + before(:each) do + ChefDK::Generator.reset + ChefDK::Generator.add_attr_to_context(:license, license) + ChefDK::Generator.add_attr_to_context(:copyright_holder, 'Adam Jacob') + end + + describe 'license_description' do + let(:license) { "all_rights" } + context "all_rights" do + it "should match the license" do + expect(helper.license_description).to match(/^Copyright \(c\)/) + end + + it "should comment if requested" do + expect(helper.license_description('#')).to match(/^# Copyright/) + end + end + + context "apache2" do + let(:license) { 'apache2' } + it "should match the license" do + expect(helper.license_description).to match(/Licensed under the Apache/) + end + + it "should comment if requested" do + expect(helper.license_description('#')).to match(/# Licensed under the Apache/) + end + end + + context "mit" do + let(:license) { 'mit' } + it "should match the license" do + expect(helper.license_description).to match(/Permission is hereby granted/) + end + + it "should comment if requested" do + expect(helper.license_description('#')).to match(/# Permission is hereby granted/) + end + end + + context "gplv2" do + let(:license) { 'gplv2' } + it "should match the license" do + expect(helper.license_description).to match(/This program is free software;/) + end + + it "should comment if requested" do + expect(helper.license_description('#')).to match(/# This program is free software;/) + end + end + + context "gplv3" do + let(:license) { 'gplv3' } + it "should match the license" do + expect(helper.license_description).to match(/This program is free software:/) + end + + it "should comment if requested" do + expect(helper.license_description('#')).to match(/# This program is free software:/) + end + end + end + +end +