Merge Recipe::DSL into Recipe

This commit is contained in:
Thibault Jouan 2014-09-22 13:13:03 +00:00
parent 4c0077d14d
commit 166dae681c
8 changed files with 150 additions and 226 deletions

View File

@ -33,7 +33,7 @@ require 'producer/core/errors'
require 'producer/core/logger_formatter' require 'producer/core/logger_formatter'
require 'producer/core/prompter' require 'producer/core/prompter'
require 'producer/core/recipe' require 'producer/core/recipe'
require 'producer/core/recipe/dsl' require 'producer/core/recipe/file_evaluator'
require 'producer/core/remote' require 'producer/core/remote'
require 'producer/core/remote/environment' require 'producer/core/remote/environment'
require 'producer/core/remote/fs' require 'producer/core/remote/fs'

View File

@ -58,7 +58,7 @@ module Producer
end end
def recipe def recipe
@recipe ||= Recipe.evaluate_from_file(@arguments.first, env) @recipe ||= Recipe::FileEvaluator.evaluate(@arguments.first, env)
end end
end end
end end

View File

@ -2,16 +2,46 @@ module Producer
module Core module Core
class Recipe class Recipe
class << self class << self
def evaluate_from_file(filepath, env) def define_macro(name, block)
dsl = DSL.new(env, File.read(filepath)).evaluate define_method(name) { |*args| task name, *args, &block }
Recipe.new(dsl.tasks)
end end
end end
attr_accessor :tasks attr_reader :env, :tasks
def initialize(tasks = []) def initialize(env)
@tasks = tasks @env = env
@tasks = []
end
def source(filepath)
instance_eval File.read("./#{filepath}.rb"), "#{filepath}.rb"
end
def target(hostname)
env.target ||= hostname
end
def task(name, *args, &block)
@tasks << Task.evaluate(name, env, *args, &block)
end
def macro(name, &block)
define_singleton_method(name) do |*args|
task("#{name}", *args, &block)
end
end
def test_macro(name, dsl: Condition::DSL, &block)
dsl.define_test(name, block)
end
def set(key, value)
env[key] = value
end
def get(key)
env[key]
end end
end end
end end

View File

@ -1,61 +0,0 @@
module Producer
module Core
class Recipe
class DSL
class << self
def define_macro(name, block)
define_method(name) { |*args| task name, *args, &block }
end
end
attr_reader :env, :code, :block, :tasks
def initialize(env, code = nil, &block)
@env = env
@code = code
@block = block
@tasks = []
end
def evaluate
if @code
instance_eval @code
else
instance_eval &@block
end
self
end
def source(filepath)
instance_eval File.read("./#{filepath}.rb"), "#{filepath}.rb"
end
def target(hostname)
env.target ||= hostname
end
def task(name, *args, &block)
@tasks << Task.evaluate(name, env, *args, &block)
end
def macro(name, &block)
define_singleton_method(name) do |*args|
task("#{name}", *args, &block)
end
end
def test_macro(name, dsl: Condition::DSL, &block)
dsl.define_test(name, block)
end
def set(key, value)
env[key] = value
end
def get(key)
env[key]
end
end
end
end
end

View File

@ -0,0 +1,14 @@
module Producer
module Core
class Recipe
class FileEvaluator
class << self
def evaluate(file_path, env)
content = File.read(file_path)
Recipe.new(env).tap { |o| o.instance_eval content, file_path }
end
end
end
end
end
end

View File

@ -1,140 +0,0 @@
require 'spec_helper'
module Producer::Core
class Recipe
describe DSL do
include FixturesHelpers
let(:code) { proc { :some_recipe_code } }
let(:env) { Env.new }
subject(:dsl) { DSL.new(env, &code) }
describe '#initialize' do
it 'assigns the given env' do
expect(dsl.env).to be env
end
it 'assigns no task' do
expect(dsl.tasks).to be_empty
end
context 'when a string of code is given as argument' do
let(:code) { 'some_code' }
subject(:dsl) { described_class.new(env, code) }
it 'assigns the string of code' do
expect(dsl.code).to eq code
end
end
context 'when a code block is given as argument' do
it 'assigns the code block' do
expect(dsl.block).to be code
end
end
end
describe '#tasks' do
let(:code) { proc { task(:some_task) {} } }
it 'returns registered tasks' do
dsl.evaluate
expect(dsl.tasks[0].name).to eq :some_task
end
end
describe '#evaluate' do
it 'evaluates its code' do
dsl = described_class.new(env) { throw :recipe_code }
expect { dsl.evaluate }.to throw_symbol :recipe_code
end
it 'returns itself' do
expect(dsl.evaluate).to eq dsl
end
end
describe '#source' do
let(:filepath) { fixture_path_for 'recipes/throw' }
it 'sources the recipe given as argument' do
expect { dsl.source filepath }.to throw_symbol :recipe_code
end
end
describe '#target' do
let(:host) { 'some_host.example' }
context 'when env has no assigned target' do
it 'registers the target host in the env' do
dsl.target host
expect(env.target).to eq host
end
end
context 'when env has an assigned target' do
before { env.target = 'already_assigned_host.example' }
it 'does not change env target' do
expect { dsl.target host }.not_to change { env.target }
end
end
end
describe '#task' do
it 'registers a new evaluated task' do
expect { dsl.task(:some_task) { :some_task_code } }
.to change { dsl.tasks.count }.by 1
end
end
describe '#macro' do
it 'defines the new recipe keyword' do
dsl.macro :hello
expect(dsl).to respond_to(:hello)
end
context 'when a defined macro is called' do
before { dsl.macro(:hello) { :some_macro_code } }
it 'registers the new task' do
expect { dsl.hello }.to change { dsl.tasks.count }.by 1
end
end
context 'when a defined macro is called with arguments' do
before { dsl.macro(:hello) { |a, b| echo a, b } }
it 'evaluates task code with arguments' do
dsl.hello :some, :args
expect(dsl.tasks.first.actions.first.arguments).to eq [:some, :args]
end
end
end
describe '#test_macro' do
it 'defines the new test' do
condition_dsl = double 'condition dsl'
test_block = proc {}
expect(condition_dsl)
.to receive(:define_test).with(:some_test, test_block)
dsl.test_macro(:some_test, dsl: condition_dsl, &test_block)
end
end
describe '#set' do
it 'registers a key/value pair in env registry' do
dsl.set :some_key, :some_value
expect(env[:some_key]).to eq :some_value
end
end
describe '#get' do
it 'fetches a value from the registry at given index' do
dsl.set :some_key, :some_value
expect(dsl.get :some_key).to eq :some_value
end
end
end
end
end

View File

@ -0,0 +1,22 @@
require 'spec_helper'
module Producer::Core
class Recipe
describe FileEvaluator do
include FixturesHelpers
describe '.evaluate' do
let(:env) { Env.new }
let(:file_path) { fixture_path_for 'recipes/some_recipe.rb' }
subject(:recipe) { described_class.evaluate(file_path, env) }
it 'returns an evaluated recipe' do
expect(recipe.tasks).to match [
an_object_having_attributes(name: :some_task),
an_object_having_attributes(name: :another_task)
]
end
end
end
end
end

View File

@ -4,33 +4,92 @@ module Producer::Core
describe Recipe do describe Recipe do
include FixturesHelpers include FixturesHelpers
subject(:recipe) { Recipe.new } let(:env) { Env.new }
subject(:recipe) { described_class.new(env) }
describe '.evaluate_from_file' do describe '#initialize' do
let(:env) { double 'env' } it 'assigns no task' do
let(:filepath) { fixture_path_for 'recipes/some_recipe.rb' } expect(recipe.tasks).to be_empty
subject(:recipe) { Recipe.evaluate_from_file(filepath, env) }
it 'returns an evaluated recipe' do
expect(recipe.tasks.map(&:name)).to eq [:some_task, :another_task]
end end
end end
describe '#initialize' do describe '#source' do
context 'without arguments' do let(:filepath) { fixture_path_for 'recipes/throw' }
it 'assigns no task' do
expect(recipe.tasks).to be_empty it 'sources the recipe given as argument' do
expect { recipe.source filepath }.to throw_symbol :recipe_code
end
end
describe '#target' do
let(:host) { 'some_host.example' }
context 'when env has no assigned target' do
it 'registers the target host in the env' do
recipe.target host
expect(env.target).to eq host
end end
end end
context 'when tasks are given as argument' do context 'when env has an assigned target' do
let(:tasks) { [double('task')] } before { env.target = 'already_assigned_host.example' }
subject(:recipe) { Recipe.new(tasks) }
it 'assigns the tasks' do it 'does not change env target' do
expect(recipe.tasks).to eq tasks expect { recipe.target host }.not_to change { env.target }
end end
end end
end end
describe '#task' do
it 'registers a new evaluated task' do
expect { recipe.task(:some_task) { :some_task_code } }
.to change { recipe.tasks.count }.by 1
end
end
describe '#macro' do
it 'defines the new recipe keyword' do
recipe.macro :hello
expect(recipe).to respond_to(:hello)
end
context 'when a defined macro is called' do
before { recipe.macro(:hello) { :some_macro_code } }
it 'registers the new task' do
expect { recipe.hello }.to change { recipe.tasks.count }.by 1
end
end
context 'when a defined macro is called with arguments' do
before { recipe.macro(:hello) { |a, b| echo a, b } }
it 'evaluates task code with given arguments' do
recipe.hello :foo, :bar
expect(recipe.tasks.first.actions.first.arguments).to eq %i[foo bar]
end
end
end
describe '#test_macro' do
it 'defines the new test' do
recipe.test_macro(:some_test) { }
expect(Condition::DSL.new(env)).to respond_to :some_test
end
end
describe '#set' do
it 'registers a key/value pair in env registry' do
recipe.set :some_key, :some_value
expect(env[:some_key]).to eq :some_value
end
end
describe '#get' do
it 'fetches a value from the registry at given index' do
recipe.set :some_key, :some_value
expect(recipe.get :some_key).to eq :some_value
end
end
end end
end end