From 2939123574e3f5f43e84c8ef33b59f5c584a437b Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Tue, 30 Jul 2013 19:29:30 +0000 Subject: [PATCH] Extract recipe DSL code and spec --- lib/producer/core.rb | 1 + lib/producer/core/recipe.rb | 35 ----------- lib/producer/core/recipe/dsl.rb | 39 ++++++++++++ spec/producer/core/recipe/dsl_spec.rb | 88 +++++++++++++++++++++++++++ spec/producer/core/recipe_spec.rb | 86 +------------------------- 5 files changed, 130 insertions(+), 119 deletions(-) create mode 100644 lib/producer/core/recipe/dsl.rb create mode 100644 spec/producer/core/recipe/dsl_spec.rb diff --git a/lib/producer/core.rb b/lib/producer/core.rb index 908c36b..c9ab4c0 100644 --- a/lib/producer/core.rb +++ b/lib/producer/core.rb @@ -1,5 +1,6 @@ require 'producer/core/cli' require 'producer/core/env' require 'producer/core/recipe' +require 'producer/core/recipe/dsl' require 'producer/core/task' require 'producer/core/version' diff --git a/lib/producer/core/recipe.rb b/lib/producer/core/recipe.rb index ab0264c..f50333f 100644 --- a/lib/producer/core/recipe.rb +++ b/lib/producer/core/recipe.rb @@ -18,41 +18,6 @@ module Producer dsl = DSL.new(@code).evaluate(env) dsl.tasks.map(&:evaluate) end - - - class DSL - attr_reader :tasks - - def initialize(code = nil, &block) - @code = code - @block = block - @tasks = [] - end - - def evaluate(env) - if @code - instance_eval @code, env.current_recipe.filepath - else - instance_eval &@block - end - self - rescue NameError => e - err = RecipeEvaluationError.new("invalid recipe keyword `#{e.name}'") - err.set_backtrace e.backtrace.reject { |l| l =~ /\/producer-core\// } - raise err - end - - - private - - def source(filepath) - instance_eval File.read("./#{filepath}.rb") - end - - def task(name, &block) - @tasks << Task.new(name, &block) - end - end end end end diff --git a/lib/producer/core/recipe/dsl.rb b/lib/producer/core/recipe/dsl.rb new file mode 100644 index 0000000..68490fb --- /dev/null +++ b/lib/producer/core/recipe/dsl.rb @@ -0,0 +1,39 @@ +module Producer + module Core + class Recipe + class DSL + attr_reader :tasks + + def initialize(code = nil, &block) + @code = code + @block = block + @tasks = [] + end + + def evaluate(env) + if @code + instance_eval @code, env.current_recipe.filepath + else + instance_eval &@block + end + self + rescue NameError => e + err = RecipeEvaluationError.new("invalid recipe keyword `#{e.name}'") + err.set_backtrace e.backtrace.reject { |l| l =~ /\/producer-core\// } + raise err + end + + + private + + def source(filepath) + instance_eval File.read("./#{filepath}.rb") + end + + def task(name, &block) + @tasks << Task.new(name, &block) + end + end + end + end +end diff --git a/spec/producer/core/recipe/dsl_spec.rb b/spec/producer/core/recipe/dsl_spec.rb new file mode 100644 index 0000000..270620f --- /dev/null +++ b/spec/producer/core/recipe/dsl_spec.rb @@ -0,0 +1,88 @@ +require 'spec_helper' + +module Producer::Core + describe Recipe::DSL do + include FixturesHelpers + + let(:code) { Proc.new { } } + let(:env) { double('env').as_null_object } + subject(:dsl) { Recipe::DSL.new &code } + + describe '#initialize' do + context 'when a string of code is given as argument' do + subject(:dsl) { Recipe::DSL.new 'nil' } + + it 'returns the DSL instance' do + expect(dsl).to be_a Recipe::DSL + end + end + + context 'when a code block is given as argument' do + subject(:dsl) { Recipe::DSL.new Proc.new { } } + + it 'returns the DSL instance' do + expect(dsl).to be_a Recipe::DSL + end + end + end + + describe '#evaluate' do + it 'evaluates its code' do + dsl = Recipe::DSL.new { raise 'error from recipe' } + expect { dsl.evaluate(env) }.to raise_error(RuntimeError, 'error from recipe') + end + + it 'returns itself' do + expect(dsl.evaluate(env)).to eq dsl + end + + context 'invalid recipe' do + let(:filepath) { fixture_path_for 'recipes/error.rb' } + let(:recipe) { Recipe.from_file(filepath) } + subject(:dsl) { Recipe::DSL.new File.read(filepath) } + + it 'reports the recipe file path in the error' do + allow(env).to receive(:current_recipe) { recipe } + expect { dsl.evaluate(env) }.to raise_error(RuntimeError) { |e| + expect(e.backtrace.first).to match /\A#{filepath}/ + } + end + + it 'raises a RecipeEvaluationError on NameError' do + dsl = Recipe::DSL.new { incorrect_keyword } + expect { dsl.evaluate(env) }.to raise_error(Recipe::RecipeEvaluationError) + end + end + end + + context 'DSL specific methods' do + subject(:dsl) { Recipe::DSL.new(&code).evaluate(env) } + + describe '#source' do + let(:code) { "source '#{fixture_path_for 'recipes/error'}'" } + subject(:dsl) { Recipe::DSL.new code } + + it 'sources the recipe given as argument' do + expect { dsl.evaluate(env) }.to raise_error(RuntimeError, 'error from recipe') + end + end + + describe '#tasks' do + let(:code) { Proc.new { task(:some_task) } } + + it 'returns registered tasks' do + expect(dsl.tasks[0].name).to eq :some_task + end + end + + describe '#task' do + let(:code) { Proc.new { task(:first); task(:last) } } + + it 'registers tasks in declaration order' do + expect(dsl.tasks[0].name).to eq :first + expect(dsl.tasks[1].name).to eq :last + end + end + end + end +end diff --git a/spec/producer/core/recipe_spec.rb b/spec/producer/core/recipe_spec.rb index f5ccdca..9679e49 100644 --- a/spec/producer/core/recipe_spec.rb +++ b/spec/producer/core/recipe_spec.rb @@ -5,7 +5,6 @@ module Producer::Core include FixturesHelpers let(:code) { 'nil' } - let(:env) { double('env').as_null_object } subject(:recipe) { Recipe.new(code) } describe '.from_file' do @@ -34,6 +33,8 @@ module Producer::Core end describe '#evaluate' do + let(:env) { double('env').as_null_object } + it 'builds a recipe DSL sandbox' do expect(Recipe::DSL).to receive(:new).once.with(code).and_call_original recipe.evaluate(env) @@ -55,88 +56,5 @@ module Producer::Core recipe.evaluate(env) end end - - - describe Recipe::DSL do - let(:code) { Proc.new { } } - subject(:dsl) { Recipe::DSL.new &code } - - describe '#initialize' do - context 'when a string of code is given as argument' do - subject(:dsl) { Recipe::DSL.new 'nil' } - - it 'returns the DSL instance' do - expect(dsl).to be_a Recipe::DSL - end - end - - context 'when a code block is given as argument' do - subject(:dsl) { Recipe::DSL.new Proc.new { } } - - it 'returns the DSL instance' do - expect(dsl).to be_a Recipe::DSL - end - end - end - - describe '#evaluate' do - it 'evaluates its code' do - dsl = Recipe::DSL.new { raise 'error from recipe' } - expect { dsl.evaluate(env) }.to raise_error(RuntimeError, 'error from recipe') - end - - it 'returns itself' do - expect(dsl.evaluate(env)).to eq dsl - end - - context 'invalid recipe' do - let(:filepath) { fixture_path_for 'recipes/error.rb' } - let(:recipe) { Recipe.from_file(filepath) } - subject(:dsl) { Recipe::DSL.new File.read(filepath) } - - it 'reports the recipe file path in the error' do - allow(env).to receive(:current_recipe) { recipe } - expect { dsl.evaluate(env) }.to raise_error(RuntimeError) { |e| - expect(e.backtrace.first).to match /\A#{filepath}/ - } - end - - it 'raises a RecipeEvaluationError on NameError' do - dsl = Recipe::DSL.new { incorrect_keyword } - expect { dsl.evaluate(env) }.to raise_error(Recipe::RecipeEvaluationError) - end - end - end - - context 'DSL specific methods' do - subject(:dsl) { Recipe::DSL.new(&code).evaluate(env) } - - describe '#source' do - let(:code) { "source '#{fixture_path_for 'recipes/error'}'" } - subject(:dsl) { Recipe::DSL.new code } - - it 'sources the recipe given as argument' do - expect { dsl.evaluate(env) }.to raise_error(RuntimeError, 'error from recipe') - end - end - - describe '#tasks' do - let(:code) { Proc.new { task(:some_task) } } - - it 'returns registered tasks' do - expect(dsl.tasks[0].name).to eq :some_task - end - end - - describe '#task' do - let(:code) { Proc.new { task(:first); task(:last) } } - - it 'registers tasks in declaration order' do - expect(dsl.tasks[0].name).to eq :first - expect(dsl.tasks[1].name).to eq :last - end - end - end - end end end