From 0921be9ab02768c776f21e42f875b31a6ce23e64 Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Mon, 22 Jul 2013 14:59:54 +0000 Subject: [PATCH] Implement recipe evaluation feature --- features/recipe.feature | 10 ++++++++ features/steps/recipe_steps.rb | 7 ++++++ lib/producer/core.rb | 3 ++- lib/producer/core/cli.rb | 2 ++ lib/producer/core/recipe.rb | 19 +++++++++++++++ spec/fixtures/recipes/empty.rb | 1 + spec/producer/core/cli_spec.rb | 16 ++++++++++++- spec/producer/core/recipe_spec.rb | 39 +++++++++++++++++++++++++++++++ 8 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 features/recipe.feature create mode 100644 features/steps/recipe_steps.rb create mode 100644 lib/producer/core/recipe.rb create mode 100644 spec/fixtures/recipes/empty.rb create mode 100644 spec/producer/core/recipe_spec.rb diff --git a/features/recipe.feature b/features/recipe.feature new file mode 100644 index 0000000..8984e54 --- /dev/null +++ b/features/recipe.feature @@ -0,0 +1,10 @@ +Feature: recipe evaluation + + Scenario: evaluates ruby code in a recipe + Given a recipe with: + """ + puts 'hello from recipe' + """ + When I execute the recipe + Then the exit status must be 0 + And the output must contain "hello from recipe" diff --git a/features/steps/recipe_steps.rb b/features/steps/recipe_steps.rb new file mode 100644 index 0000000..5ade95c --- /dev/null +++ b/features/steps/recipe_steps.rb @@ -0,0 +1,7 @@ +Given(/^a recipe with:$/) do |recipe_body| + write_file 'recipe.rb', recipe_body +end + +When(/^I execute the recipe$/) do + run_simple('producer localhost recipe.rb', false) +end diff --git a/lib/producer/core.rb b/lib/producer/core.rb index 20feea1..6f3f21b 100644 --- a/lib/producer/core.rb +++ b/lib/producer/core.rb @@ -2,6 +2,7 @@ require 'producer/core/version' module Producer module Core - autoload :CLI, 'producer/core/cli' + autoload :CLI, 'producer/core/cli' + autoload :Recipe, 'producer/core/recipe' end end diff --git a/lib/producer/core/cli.rb b/lib/producer/core/cli.rb index d8014a3..d1451ba 100644 --- a/lib/producer/core/cli.rb +++ b/lib/producer/core/cli.rb @@ -12,6 +12,8 @@ module Producer def run! print_usage_and_exit(64) unless @arguments.length == 2 + + Recipe.from_file(@arguments[1]).evaluate end diff --git a/lib/producer/core/recipe.rb b/lib/producer/core/recipe.rb new file mode 100644 index 0000000..2d93c5e --- /dev/null +++ b/lib/producer/core/recipe.rb @@ -0,0 +1,19 @@ +module Producer + module Core + class Recipe + attr_reader :code + + def self.from_file(filepath) + new(File.read(filepath)) + end + + def initialize(code) + @code = code + end + + def evaluate + Object.new.instance_eval @code + end + end + end +end diff --git a/spec/fixtures/recipes/empty.rb b/spec/fixtures/recipes/empty.rb new file mode 100644 index 0000000..5077886 --- /dev/null +++ b/spec/fixtures/recipes/empty.rb @@ -0,0 +1 @@ +# can contain tasks written in ruby language. diff --git a/spec/producer/core/cli_spec.rb b/spec/producer/core/cli_spec.rb index d5fe192..a031075 100644 --- a/spec/producer/core/cli_spec.rb +++ b/spec/producer/core/cli_spec.rb @@ -2,7 +2,9 @@ require 'spec_helper' module Producer::Core describe CLI do - let(:arguments) { %w{host recipe.rb} } + include FixturesHelpers + + let(:arguments) { ['host', fixture_path_for('recipes/empty.rb')] } subject(:cli) { CLI.new(arguments) } describe '#initialize' do @@ -12,6 +14,18 @@ module Producer::Core end describe '#run!' do + it 'builds a recipe' do + expect(Recipe).to receive(:from_file).with(arguments[1]).and_call_original + cli.run! + end + + it 'evaluates the recipe' do + recipe = double('recipe') + allow(Recipe).to receive(:from_file).and_return(recipe) + expect(recipe).to receive(:evaluate) + cli.run! + end + context 'missing argument' do let(:arguments) { %w{host} } let(:stdout) { StringIO.new } diff --git a/spec/producer/core/recipe_spec.rb b/spec/producer/core/recipe_spec.rb new file mode 100644 index 0000000..738c191 --- /dev/null +++ b/spec/producer/core/recipe_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +module Producer::Core + describe Recipe do + include FixturesHelpers + + let(:code) { 'some ruby code' } + subject(:recipe) { Recipe.new(code) } + + describe '.from_file' do + it 'builds a recipe whose code is read from given file path' do + filepath = fixture_path_for 'recipes/empty.rb' + recipe = Recipe.from_file(filepath) + expect(recipe.code).to eq File.read(filepath) + end + end + + describe '#initialize' do + it 'builds a recipe' do + expect(recipe).to be_a Recipe + end + end + + describe '#code' do + it 'returns the assigned code' do + expect(recipe.code).to eq code + end + end + + describe '#evaluate' do + let(:message) { 'error from recipe' } + let(:code) { "raise '#{message}'" } + + it 'evaluates its code' do + expect { recipe.evaluate }.to raise_error(RuntimeError, 'error from recipe') + end + end + end +end