Breaking up IRecipeManager in two components: IRecipeHarvester (harvest and parse) and IRecipeManager (execute)

Renaming DiscoverRecipes to HarvestRecipes for consistency with other harvesters we have in Orchard.
Renaming MetaData to Metadata for the recipe step and its handler.
Moving recipe information elements in the recipe to under the Recipe element, renaming root element to Orchard and modifying the parser accordingly.
Using element.Name.LocalName in the parser.
Fixing whitespaces in a couple places, setting the Orchard.Recipes assembly copyright information and making the assembly securitytransparent.
Refactoring code and unit tests as a result of to above.

--HG--
branch : recipe
This commit is contained in:
Suha Can
2011-02-12 13:42:33 -08:00
parent 096e5e1d42
commit 578c107da3
12 changed files with 137 additions and 95 deletions

View File

@@ -1,19 +1,21 @@
<?xml version="1.0"?>
<Recipe>
<Name>cms</Name>
<Description>a sample Orchard recipe describing a cms</Description>
<Author>orchard</Author>
<WebSite>http://orchardproject.net</WebSite>
<Tags>tag1, tag2</Tags>
<Version>1.1</Version>
<Orchard>
<Recipe>
<Name>cms</Name>
<Description>a sample Orchard recipe describing a cms</Description>
<Author>orchard</Author>
<WebSite>http://orchardproject.net</WebSite>
<Tags>tag1, tag2</Tags>
<Version>1.1</Version>
</Recipe>
<!-- Steps -->
<Module src="source directory" replace="false" />
<Module name="module1" repository="somerepo" version="1.1" replace="true" />
<Feature disable="f1, f2" enable="f3,f4" />
<MetaData>
<Metadata>
<Types>
<Blog creatable="true">
<Body format="abodyformat"/>
@@ -23,26 +25,27 @@
</Parts>
<Fields>
</Fields>
</MetaData>
</Metadata>
<Command>
command1
command2
command3
</Command>
<Settings>
<SiteSettingsPart PageSize="30" />
<CommentSettingsPart enableSpamProtection="true" />
</Settings>
<Migration feature="f2"/>
<Theme src="source dir" enabled="true" current="true" />
<Theme name="theme1" repository="somerepo" replace="true" />
<CleanUpInactive />
<Custom1 attr1="value1" />
<Custom2 attr2="value2" />
</Recipe>
</Orchard>

View File

@@ -18,6 +18,7 @@ namespace Orchard.Tests.Modules.Recipes.Services {
public class RecipeManagerTests {
private IContainer _container;
private IRecipeManager _recipeManager;
private IRecipeHarvester _recipeHarvester;
private IRecipeParser _recipeParser;
private IExtensionFolders _folders;
@@ -61,6 +62,7 @@ namespace Orchard.Tests.Modules.Recipes.Services {
var builder = new ContainerBuilder();
_folders = new ModuleFolders(new[] { _tempFolderName }, new StubCacheManager(), new StubWebSiteFolder());
builder.RegisterType<RecipeManager>().As<IRecipeManager>();
builder.RegisterType<RecipeHarvester>().As<IRecipeHarvester>();
builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterInstance(_folders).As<IExtensionFolders>();
@@ -72,6 +74,7 @@ namespace Orchard.Tests.Modules.Recipes.Services {
_container = builder.Build();
_recipeManager = _container.Resolve<IRecipeManager>();
_recipeParser = _container.Resolve<IRecipeParser>();
_recipeHarvester = _container.Resolve<IRecipeHarvester>();
}
[TearDown]
@@ -80,21 +83,21 @@ namespace Orchard.Tests.Modules.Recipes.Services {
}
[Test]
public void DiscoverRecipesFailsToFindRecipesWhenCalledWithNotExistingExtension() {
var recipes = (List<Recipe>) _recipeManager.DiscoverRecipes("cantfindme");
public void HarvestRecipesFailsToFindRecipesWhenCalledWithNotExistingExtension() {
var recipes = (List<Recipe>) _recipeHarvester.HarvestRecipes("cantfindme");
Assert.That(recipes.Count, Is.EqualTo(0));
}
[Test]
public void DiscoverRecipesShouldDiscoverRecipeXmlFiles() {
var recipes = (List<Recipe>)_recipeManager.DiscoverRecipes("Sample1");
public void HarvestRecipesShouldHarvestRecipeXmlFiles() {
var recipes = (List<Recipe>)_recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count, Is.EqualTo(1));
}
[Test]
public void ParseRecipeLoadsRecipeMetaDataIntoModel() {
var recipes = (List<Recipe>) _recipeManager.DiscoverRecipes("Sample1");
var recipes = (List<Recipe>) _recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count, Is.EqualTo(1));
var sampleRecipe = recipes[0];
@@ -108,7 +111,7 @@ namespace Orchard.Tests.Modules.Recipes.Services {
[Test]
public void ParseRecipeLoadsRecipeStepsIntoModel() {
var recipes = (List<Recipe>)_recipeManager.DiscoverRecipes("Sample1");
var recipes = (List<Recipe>)_recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count, Is.EqualTo(1));
var sampleRecipe = recipes[0];
@@ -124,7 +127,7 @@ namespace Orchard.Tests.Modules.Recipes.Services {
[Test]
public void ExecuteInvokesHandlersWithSteps() {
var recipes = (List<Recipe>)_recipeManager.DiscoverRecipes("Sample1");
var recipes = (List<Recipe>)_recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count, Is.EqualTo(1));
var sampleRecipe = recipes[0];

View File

@@ -7,6 +7,6 @@ OrchardVersion: 1.0.20
Description: Provides Orchard Recipes.
Features:
Orchard.Recipes:
Name: Recipes
Name: Recipes
Description: Implementation of Orchard recipes.
Category: Core

View File

@@ -55,11 +55,12 @@
<Compile Include="RecipeHandlers\CleanUpInactiveRecipeHandler.cs" />
<Compile Include="RecipeHandlers\CommandRecipeHandler.cs" />
<Compile Include="RecipeHandlers\FeatureRecipeHandler.cs" />
<Compile Include="RecipeHandlers\MetaDataRecipeHandler.cs" />
<Compile Include="RecipeHandlers\MetadataRecipeHandler.cs" />
<Compile Include="RecipeHandlers\MigrationRecipeHandler.cs" />
<Compile Include="RecipeHandlers\ModuleRecipeHandler.cs" />
<Compile Include="RecipeHandlers\SettingsRecipeHandler.cs" />
<Compile Include="RecipeHandlers\ThemeRecipeHandler.cs" />
<Compile Include="Services\RecipeHarvester.cs" />
<Compile Include="Services\RecipeManager.cs" />
<Compile Include="Services\RecipeParser.cs" />
</ItemGroup>

View File

@@ -1,6 +1,6 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
@@ -8,9 +8,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Orchard.Recipes")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("MSIT")]
[assembly: AssemblyProduct("Orchard.Recipes")]
[assembly: AssemblyCopyright("Copyright © MSIT 2011")]
[assembly: AssemblyProduct("Orchard")]
[assembly: AssemblyCopyright("Copyright © Outercurve Foundation 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -31,5 +30,6 @@ using System.Runtime.InteropServices;
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.0.20")]
[assembly: AssemblyFileVersion("1.0.20")]
[assembly: SecurityTransparent]

View File

@@ -4,8 +4,8 @@ using Orchard.Recipes.Models;
using Orchard.Recipes.Services;
namespace Orchard.Recipes.RecipeHandlers {
public class MetaDataRecipeHandler : IRecipeHandler {
public MetaDataRecipeHandler() {
public class MetadataRecipeHandler : IRecipeHandler {
public MetadataRecipeHandler() {
Logger = NullLogger.Instance;
T = NullLocalizer.Instance;
}
@@ -13,7 +13,7 @@ namespace Orchard.Recipes.RecipeHandlers {
public Localizer T { get; set; }
ILogger Logger { get; set; }
// handles the <MetaData> step
// handles the <Metadata> step
public void ExecuteRecipeStep(RecipeContext recipeContext) {
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Orchard.Environment.Extensions;
using Orchard.FileSystems.WebSite;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Recipes.Models;
namespace Orchard.Recipes.Services {
public class RecipeHarvester : IRecipeHarvester {
private readonly IExtensionManager _extensionManager;
private readonly IWebSiteFolder _webSiteFolder;
private readonly IRecipeParser _recipeParser;
public RecipeHarvester(
IExtensionManager extensionManager,
IWebSiteFolder webSiteFolder,
IRecipeParser recipeParser) {
_extensionManager = extensionManager;
_webSiteFolder = webSiteFolder;
_recipeParser = recipeParser;
Logger = NullLogger.Instance;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
ILogger Logger { get; set; }
public IEnumerable<Recipe> HarvestRecipes(string extensionName) {
var recipes = new List<Recipe>();
var extension = _extensionManager.GetExtension(extensionName);
if (extension != null) {
var recipeLocation = Path.Combine(extension.Location, extensionName, "Recipes");
var recipeFiles = _webSiteFolder.ListFiles(recipeLocation, true);
recipes.AddRange(
from recipeFile in recipeFiles
where recipeFile.EndsWith(".recipe.xml", StringComparison.OrdinalIgnoreCase)
select _recipeParser.ParseRecipe(_webSiteFolder.ReadFile(recipeFile)));
}
else {
Logger.Error("Could not discover recipes because module '{0}' was not found.", extensionName);
}
return recipes;
}
}
}

View File

@@ -1,27 +1,13 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Orchard.Environment.Extensions;
using Orchard.FileSystems.WebSite;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Recipes.Models;
namespace Orchard.Recipes.Services {
public class RecipeManager : IRecipeManager {
private readonly IExtensionManager _extensionManager;
private readonly IWebSiteFolder _webSiteFolder;
private readonly IRecipeParser _recipeParser;
private readonly IEnumerable<IRecipeHandler> _recipeHandlers;
public RecipeManager(
IExtensionManager extensionManager,
IWebSiteFolder webSiteFolder,
IRecipeParser recipeParser,
IEnumerable<IRecipeHandler> recipeHandlers) {
_extensionManager = extensionManager;
_webSiteFolder = webSiteFolder;
_recipeParser = recipeParser;
public RecipeManager(IEnumerable<IRecipeHandler> recipeHandlers) {
_recipeHandlers = recipeHandlers;
Logger = NullLogger.Instance;
@@ -36,6 +22,7 @@ namespace Orchard.Recipes.Services {
// TODO: Run each step inside a transaction boundary.
// TODO: Output should go into a report.
// TODO: Eventually return a guid.tostring("n") execution id
foreach (var recipeStep in recipe.RecipeSteps) {
recipeContext.RecipeStep = recipeStep;
recipeContext.Executed = false;
@@ -47,23 +34,5 @@ namespace Orchard.Recipes.Services {
}
}
}
public IEnumerable<Recipe> DiscoverRecipes(string extensionName) {
var recipes = new List<Recipe>();
var extension = _extensionManager.GetExtension(extensionName);
if (extension != null) {
var recipeLocation = Path.Combine(extension.Location, extensionName, "Recipes");
var recipeFiles = _webSiteFolder.ListFiles(recipeLocation, true);
recipes.AddRange(
from recipeFile in recipeFiles
where recipeFile.EndsWith(".recipe.xml")
select _recipeParser.ParseRecipe(_webSiteFolder.ReadFile(recipeFile)));
}
else {
Logger.Error("Could not discover recipes because module '{0}' was not found.", extensionName);
}
return recipes;
}
}
}

View File

@@ -26,29 +26,38 @@ namespace Orchard.Recipes.Services {
textReader.Close();
foreach (var element in recipeTree.Elements()) {
switch(element.Name.ToString()) {
case "Name":
recipe.Name = element.Value;
break;
case "Description":
recipe.Description = element.Value;
break;
case "Author":
recipe.Author = element.Value;
break;
case "WebSite":
recipe.WebSite = element.Value;
break;
case "Version":
recipe.Version = element.Value;
break;
case "Tags":
recipe.Tags = element.Value;
break;
default:
var recipeStep = new RecipeStep {Name = element.Name.ToString(), Step = element};
recipeSteps.Add(recipeStep);
break;
// Recipe mETaDaTA
if (element.Name.LocalName == "Recipe") {
foreach (var metadataElement in element.Elements()) {
switch (metadataElement.Name.LocalName) {
case "Name":
recipe.Name = metadataElement.Value;
break;
case "Description":
recipe.Description = metadataElement.Value;
break;
case "Author":
recipe.Author = metadataElement.Value;
break;
case "WebSite":
recipe.WebSite = metadataElement.Value;
break;
case "Version":
recipe.Version = metadataElement.Value;
break;
case "Tags":
recipe.Tags = metadataElement.Value;
break;
default:
Logger.Error("Unrecognized recipe metadata element {0} encountered. Skipping.", metadataElement.Name.LocalName);
break;
}
}
}
// Recipe step
else {
var recipeStep = new RecipeStep { Name = element.Name.LocalName, Step = element };
recipeSteps.Add(recipeStep);
}
}
recipe.RecipeSteps = recipeSteps;

View File

@@ -195,6 +195,7 @@
<Compile Include="Recipes\Models\RecipeContext.cs" />
<Compile Include="Recipes\Models\RecipeStep.cs" />
<Compile Include="Recipes\Services\IRecipeHandler.cs" />
<Compile Include="Recipes\Services\IRecipeHarvester.cs" />
<Compile Include="Recipes\Services\IRecipeManager.cs" />
<Compile Include="Recipes\Services\IRecipeParser.cs" />
<Compile Include="Security\IEncryptionService.cs" />

View File

@@ -0,0 +1,8 @@
using System.Collections.Generic;
using Orchard.Recipes.Models;
namespace Orchard.Recipes.Services {
public interface IRecipeHarvester : IDependency {
IEnumerable<Recipe> HarvestRecipes(string extensionName);
}
}

View File

@@ -1,9 +1,7 @@
using System.Collections.Generic;
using Orchard.Recipes.Models;
using Orchard.Recipes.Models;
namespace Orchard.Recipes.Services {
public interface IRecipeManager : IDependency {
void Execute(Recipe recipe);
IEnumerable<Recipe> DiscoverRecipes(string extensionName);
}
}