Skip to content

Instantly share code, notes, and snippets.

@ssimeonov
Created June 18, 2013 22:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ssimeonov/5810145 to your computer and use it in GitHub Desktop.
Save ssimeonov/5810145 to your computer and use it in GitHub Desktop.
Chef LWRP subclassing example: rake resource subclasses rvm_shell.
class Chef
class ResourceFactory
class Rake
def self.create(*args, &block)
register
Chef::Resource::Rake.new(*args, &block)
end
def self.register
@klass ||= begin
Chef::Resource.const_set('Rake', Class.new(Chef::Resource::RvmShell) do
identity_attr :task
def self.resource_name
'rake'
end
def self.default_action
:run
end
def initialize(name, run_context=nil)
super
@provider = Chef::Provider::RvmShell
@allowed_actions = [:nothing, :run]
@task = name
@arguments = []
@bundler = true
@trace = true
@runner = nil
end
def task(arg=nil)
set_or_return(:task, arg, :kind_of => [String])
end
def arguments(*args)
set_or_return(:arguments, args.empty? ? nil : args, :kind_of => [Array]).tap do |result|
if (index = result.find_index(nil))
raise "argument ##{index} cannot be nil. (Arguments: #{result.inspect})"
end
end
end
def bundler(arg=nil)
set_or_return(:bundler, arg, :kind_of => [TrueClass, FalseClass])
end
def trace(arg=nil)
set_or_return(:trace, arg, :kind_of => [TrueClass, FalseClass])
end
def runner(arg=nil)
set_or_return(:runner, arg, :kind_of => [String, Symbol, NilClass])
end
def code(arg=nil)
raise 'code attribute of rake resource is read-only' if arg
code_to_run = [
runner ? "#{runner} run" : nil,
bundler ? 'bundle exec' : nil,
arguments.empty? ?
"rake #{task}" :
"rake #{task}[#{arguments.join(',').shellescape}]",
trace ? '--trace' : nil
].compact.join(' ')
super code_to_run
end
end)
end
end
end
end
end
def rake(*args, &block)
Chef::ResourceFactory::Rake.register
method_missing(:rake, *args, &block)
end
if defined? ChefSpec
module ChefSpec
module Matchers
RSpec::Matchers.define :execute_rake do |code|
match do |chef_run|
chef_run.resources.any? do |resource|
resource_type(resource).to_s == 'rake' and code === resource.code
end
end
end
end
end
end
#
# Cookbook Name:: rake-chef
# Recipe:: spec
#
# Copyright (C) 2013 YOUR_NAME
#
# All rights reserved - Do Not Redistribute
#
rake 'db:seed' do
runner :foreman
end
require_relative 'spec_helper'
describe 'rake-chef::spec' do
let (:chef_run) { ChefSpec::ChefRunner.new.converge 'rake-chef::spec' }
specify { expect(chef_run).to execute_rake 'foreman run bundle exec rake db:seed --trace' }
describe 'rake resource' do
subject { Chef::ResourceFactory::Rake.create('my:task') }
it { should be_kind_of Chef::Resource::LWRPBase }
its(:task) { should == 'my:task' }
its(:arguments) { should == [] }
its(:bundler) { should be_true }
its(:trace) { should be_true }
its(:runner) { should be_nil }
its(:code) { should == 'bundle exec rake my:task --trace'}
it 'does not allow nil arguments' do
expect { subject.arguments 1, nil, 'hi' }.to raise_error /cannot be nil/
end
it 'does not allow setting the command attribute' do
expect { subject.code 'rake -T' }.to raise_error /read-only/
end
context 'with arguments 1 & "hello" and no bundler' do
before do
subject.arguments 1, 'hello there'
subject.bundler false
end
its(:arguments) { should == [1, 'hello there'] }
its(:bundler) { should be_false }
its(:code) { should == 'rake my:task[1,hello\ there] --trace'}
end
context 'with foreman runner and no trace' do
before do
subject.runner :foreman
subject.trace false
end
its(:runner) { should == :foreman }
its(:code) { should == 'foreman run bundle exec rake my:task'}
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment