diff --git a/lib/producer/core.rb b/lib/producer/core.rb index 144118d..40054f9 100644 --- a/lib/producer/core.rb +++ b/lib/producer/core.rb @@ -9,3 +9,4 @@ require 'producer/core/remote' require 'producer/core/task' require 'producer/core/task/dsl' require 'producer/core/version' +require 'producer/core/worker' diff --git a/lib/producer/core/cli.rb b/lib/producer/core/cli.rb index 9cbb012..7188ae4 100644 --- a/lib/producer/core/cli.rb +++ b/lib/producer/core/cli.rb @@ -12,16 +12,6 @@ module Producer def run! check_arguments! - evaluate_recipe_file - end - - def check_arguments! - print_usage_and_exit(64) unless @arguments.length == 1 - end - - def evaluate_recipe_file - recipe = Recipe.from_file(@arguments.first) - env = Env.new(recipe) begin recipe.evaluate env rescue RecipeEvaluationError => e @@ -31,6 +21,23 @@ module Producer exit 70 end + worker.process recipe.tasks + end + + def check_arguments! + print_usage_and_exit(64) unless @arguments.length == 1 + end + + def env + @env ||= Env.new(recipe) + end + + def recipe + @recipe ||= Recipe.from_file(@arguments.first) + end + + def worker + @worker ||= Worker.new end private diff --git a/lib/producer/core/recipe.rb b/lib/producer/core/recipe.rb index d3f27c2..05c9c3f 100644 --- a/lib/producer/core/recipe.rb +++ b/lib/producer/core/recipe.rb @@ -1,7 +1,7 @@ module Producer module Core class Recipe - attr_reader :code, :filepath + attr_reader :code, :filepath, :tasks def self.from_file(filepath) new(File.read(filepath), filepath) @@ -10,11 +10,13 @@ module Producer def initialize(code, filepath = nil) @code = code @filepath = filepath + @tasks = [] end def evaluate(env) dsl = DSL.new(@code).evaluate(env) dsl.tasks.map { |e| e.evaluate env } + @tasks = dsl.tasks end end end diff --git a/lib/producer/core/task.rb b/lib/producer/core/task.rb index 5e7440d..5bb8cd2 100644 --- a/lib/producer/core/task.rb +++ b/lib/producer/core/task.rb @@ -1,17 +1,18 @@ module Producer module Core class Task - attr_reader :name + attr_reader :name, :actions def initialize(name, &block) - @name = name - @block = block + @name = name + @block = block + @actions = [] end def evaluate(env) dsl = DSL.new(&@block) dsl.evaluate(env) - dsl.actions.map(&:apply) + @actions = dsl.actions end end end diff --git a/lib/producer/core/worker.rb b/lib/producer/core/worker.rb new file mode 100644 index 0000000..8ef174e --- /dev/null +++ b/lib/producer/core/worker.rb @@ -0,0 +1,13 @@ +module Producer + module Core + class Worker + def process(tasks) + tasks.each { |t| process_task t } + end + + def process_task(task) + task.actions.each(&:apply) + end + end + end +end diff --git a/spec/producer/core/cli_spec.rb b/spec/producer/core/cli_spec.rb index 3fedfc7..12100c3 100644 --- a/spec/producer/core/cli_spec.rb +++ b/spec/producer/core/cli_spec.rb @@ -27,10 +27,54 @@ module Producer::Core cli.run! end - it 'evaluates the recipe' do - expect(cli).to receive(:evaluate_recipe_file) + it 'evaluates the recipe with the environment' do + expect(cli.recipe).to receive(:evaluate).with(cli.env) cli.run! end + + it 'processes the tasks with the worker' do + allow(cli.recipe).to receive(:tasks) { [:some_task] } + expect(cli.worker).to receive(:process).with([:some_task]) + cli.run! + end + + context 'when recipe evaluation fails' do + let(:recipe_file) { fixture_path_for('recipes/invalid.rb') } + let(:stdout) { StringIO.new } + subject(:cli) { CLI.new(arguments, stdout) } + + context 'when error is known' do + it 'exits with a return status of 70' do + expect { cli.run! } + .to raise_error(SystemExit) { |e| + expect(e.status).to eq 70 + } + end + + it 'prints the specific error' do + trap_exit { cli.run! } + expect(stdout.string).to match(/ + \A + #{recipe_file}:4: + .+ + invalid\srecipe\skeyword\s`invalid_keyword' + /x) + end + + it 'excludes producer own source code from the error backtrace' do + trap_exit { cli.run! } + expect(stdout.string).to_not match /\/producer-core\// + end + end + + context 'when error is unknown (unexpected)' do + it 'lets the error be' do + UnexpectedError = Class.new(StandardError) + allow(cli.recipe).to receive(:evaluate).and_raise(UnexpectedError) + expect { cli.run! }.to raise_error(UnexpectedError) + end + end + end end describe '#check_arguments!' do @@ -58,67 +102,43 @@ module Producer::Core end end - describe '#evaluate_recipe_file' do + describe '#env' do + it 'builds an environment with the current recipe' do + expect(Env).to receive(:new).with(cli.recipe) + cli.env + end + + it 'returns the env' do + env = double('env') + allow(Env).to receive(:new) { env } + expect(cli.env).to be env + end + end + + describe '#recipe' do it 'builds a recipe' do expect(Recipe) - .to receive(:from_file).with(recipe_file).and_call_original - cli.evaluate_recipe_file + .to receive(:from_file).with(recipe_file) + cli.recipe end - it 'builds an environment with the current recipe' do - recipe = double('recipe').as_null_object - allow(Recipe).to receive(:from_file).and_return(recipe) - expect(Env).to receive(:new).with(recipe).and_call_original - cli.evaluate_recipe_file - end - - it 'evaluates the recipe with the environment' do + it 'returns the recipe' do recipe = double('recipe') - allow(Recipe).to receive(:from_file).and_return(recipe) - env = double('env') - allow(Env).to receive(:new).and_return(env) - expect(recipe).to receive(:evaluate).with(env) - cli.evaluate_recipe_file + allow(Recipe).to receive(:new) { recipe } + expect(cli.recipe).to be recipe + end + end + + describe '#worker' do + it 'builds a worker' do + expect(Worker).to receive(:new) + cli.worker end - context 'when recipe evaluation fails' do - let(:recipe_file) { fixture_path_for('recipes/invalid.rb') } - let(:stdout) { StringIO.new } - subject(:cli) { CLI.new(arguments, stdout) } - - context 'when error is known' do - it 'exits with a return status of 70' do - expect { cli.evaluate_recipe_file } - .to raise_error(SystemExit) { |e| - expect(e.status).to eq 70 - } - end - - it 'prints the specific error' do - trap_exit { cli.evaluate_recipe_file } - expect(stdout.string).to match(/ - \A - #{recipe_file}:4: - .+ - invalid\srecipe\skeyword\s`invalid_keyword' - /x) - end - - it 'excludes producer own source code from the error backtrace' do - trap_exit { cli.evaluate_recipe_file } - expect(stdout.string).to_not match /\/producer-core\// - end - end - - context 'when error is unknown (unexpected)' do - it 'lets the error be' do - UnexpectedError = Class.new(StandardError) - recipe = double('recipe') - allow(Recipe).to receive(:from_file).and_return(recipe) - allow(recipe).to receive(:evaluate).and_raise(UnexpectedError) - expect { cli.evaluate_recipe_file }.to raise_error(UnexpectedError) - end - end + it 'returns the worker' do + worker = double('worker') + allow(Worker).to receive(:new) { worker } + expect(cli.worker).to be worker end end end diff --git a/spec/producer/core/recipe_spec.rb b/spec/producer/core/recipe_spec.rb index 53f6daf..5a673cf 100644 --- a/spec/producer/core/recipe_spec.rb +++ b/spec/producer/core/recipe_spec.rb @@ -24,6 +24,10 @@ module Producer::Core it 'assigns nil as a default filepath' do expect(recipe.filepath).to be nil end + + it 'has no task' do + expect(recipe.tasks).to be_empty + end end describe '#code' do @@ -64,6 +68,13 @@ module Producer::Core expect(task).to receive(:evaluate).with(env) recipe.evaluate(env) end + + it 'assigns the evaluated tasks' do + dsl = Recipe::DSL.new { task(:some_task) { } } + allow(Recipe::DSL).to receive(:new) { dsl } + recipe.evaluate(env) + expect(recipe.tasks.first.name).to eq :some_task + end end end end diff --git a/spec/producer/core/task_spec.rb b/spec/producer/core/task_spec.rb index 5ca22f8..eb3112f 100644 --- a/spec/producer/core/task_spec.rb +++ b/spec/producer/core/task_spec.rb @@ -14,6 +14,10 @@ module Producer::Core it 'assigns the block' do expect(task.instance_eval { @block }).to be block end + + it 'has no action' do + expect(task.actions).to be_empty + end end describe '#name' do @@ -37,13 +41,12 @@ module Producer::Core task.evaluate(env) end - it 'applies DSL sandbox actions' do - dsl = double('task DSL').as_null_object + it 'assigns the evaluated actions' do + dsl = double('dsl').as_null_object allow(Task::DSL).to receive(:new) { dsl } - action = double('action') - allow(dsl).to receive(:actions) { [action] } - expect(action).to receive(:apply) + allow(dsl).to receive(:actions) { [:some_action] } task.evaluate(env) + expect(task.actions).to eq [:some_action] end end end diff --git a/spec/producer/core/worker_spec.rb b/spec/producer/core/worker_spec.rb new file mode 100644 index 0000000..268b785 --- /dev/null +++ b/spec/producer/core/worker_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +module Producer::Core + describe Worker do + subject(:worker) { Worker.new } + + describe '#process' do + it 'processes each task' do + expect(worker).to receive(:process_task).with(:some_task) + worker.process [:some_task] + end + end + + describe '#process_task' do + it 'applies the task actions' do + action = double('action') + task = double('task') + allow(task).to receive(:actions) { [action] } + expect(action).to receive(:apply) + worker.process_task(task) + end + end + end +end