Created
May 3, 2016 05:07
-
-
Save sczizzo/0a9d3e8bea8eb4d03591e5514e98d2bb to your computer and use it in GitHub Desktop.
How one might implement Chef's Recipe DSL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Basics | |
module Chef | |
class Resource | |
attr_reader :name | |
def initialize name | |
@name = name | |
end | |
def execute | |
raise 'To be implemented by subclass...' | |
end | |
end | |
end | |
module Chef | |
class Recipe | |
attr_reader :resources | |
def initialize resources | |
@resources = resources | |
@executed = false | |
end | |
def execute | |
return if @executed | |
@executed = true | |
@resources.each(&:execute) | |
end | |
end | |
end | |
# Builders | |
module Chef | |
class Resource | |
class Builder | |
attr_reader :resource | |
def camelize s | |
s.to_s.split('_').map(&:capitalize).join('') | |
end | |
def initialize kind, name, &block | |
klass = Chef::Resource.const_get camelize(kind) | |
@resource = klass.new name | |
@resource.instance_eval &block if block | |
end | |
end | |
end | |
end | |
module Chef | |
class Recipe | |
class Builder | |
attr_reader :recipe | |
def initialize &block | |
@resources = [] | |
instance_eval &block if block | |
@recipe = Chef::Recipe.new @resources | |
end | |
def method_missing resource, *args, &block | |
@resources << Chef::Resource::Builder.new(resource, *args, &block).resource | |
rescue NameError | |
super | |
end | |
end | |
end | |
end | |
module Chef | |
def self.recipe &block | |
Chef::Recipe::Builder.new(&block).recipe.execute | |
end | |
end | |
# Resources | |
module Chef | |
class Resource | |
class Log < Resource | |
require 'date' | |
require 'time' | |
def initialize name | |
super name | |
@level = 'INFO' | |
@precision = 3 | |
end | |
def level l | |
@level = l | |
end | |
def precision n | |
@precision = n | |
end | |
def now | |
Time.now.utc.iso8601(@precision) | |
end | |
def execute | |
puts '%s [%s] %s' % [ @level, now, @name ] | |
end | |
end | |
end | |
end | |
module Chef | |
class Resource | |
class File < Resource | |
require 'fileutils' | |
require 'date' | |
require 'time' | |
def action a | |
@action = a | |
end | |
def contents s | |
@contents = s | |
end | |
def execute | |
@action ||= :create | |
case @action | |
when :create | |
::File.open(@name, 'w') { |f| f.puts @contents } | |
puts 'Wrote file at %s' % @name.inspect | |
when :delete | |
::FileUtils.rm_rf @name | |
puts 'Deleted file at %s' % @name.inspect | |
else | |
raise 'Invalid action provided for file resource' | |
end | |
end | |
end | |
end | |
end | |
module Chef | |
class Resource | |
class RubyBlock < Resource | |
def block &block | |
@block = block | |
end | |
def execute | |
@block.call if @block | |
end | |
end | |
end | |
end | |
# Using the DSL... | |
Chef.recipe do | |
log 'hello, world!' | |
file '/tmp/foo' do | |
contents 'test' | |
end | |
ruby_block 'read_file' do | |
puts 'inside the ruby_block, but outside the block itself' | |
block do | |
contents = File.read('/tmp/foo') | |
puts "Read file: %s" % contents.inspect | |
end | |
end | |
file '/tmp/foo' do | |
action :delete | |
end | |
log 'goodbye, world!' do | |
level 'DEBUG' | |
precision 6 | |
end | |
puts inspect | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment