Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Named Profiles for option configuration #162

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
14 changes: 14 additions & 0 deletions src/Smidge.Core/DefaultProfileStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Smidge.Options;

namespace Smidge
{

/// <summary>
/// An implementation of ISmidgeProfileStrategy that will always use the Default profile.
/// </summary>
/// <seealso cref="ISmidgeProfileStrategy" />
public class DefaultProfileStrategy : ISmidgeProfileStrategy
{
public string GetCurrentProfileName() => SmidgeOptionsProfile.Default;
}
}
6 changes: 3 additions & 3 deletions src/Smidge.Core/FileProcessors/PreProcessManager.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
Expand Down Expand Up @@ -35,7 +35,7 @@ public async Task ProcessAndCacheFileAsync(IWebFile file, BundleOptions bundleOp
if (file == null) throw new ArgumentNullException(nameof(file));
if (file.Pipeline == null) throw new ArgumentNullException($"{nameof(file)}.Pipeline");

await ProcessFile(file, _bundleManager.GetAvailableOrDefaultBundleOptions(bundleOptions, false), bundleContext);
await ProcessFile(file, _bundleManager.GetAvailableOrDefaultBundleOptions(bundleOptions), bundleContext);
}

private async Task ProcessFile(IWebFile file, BundleOptions bundleOptions, BundleContext bundleContext)
Expand Down Expand Up @@ -127,4 +127,4 @@ private static void FileModified(WatchedFile file)
file.BundleOptions.FileWatchOptions.Changed(new FileWatchEventArgs(file));
}
}
}
}
19 changes: 10 additions & 9 deletions src/Smidge.Core/FileProcessors/PreProcessPipelineFactory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Smidge.Models;
using System.Linq;
Expand Down Expand Up @@ -67,19 +67,20 @@ private PreProcessPipeline GetDefault(WebFileType fileType)
switch (fileType)
{
case WebFileType.Js:

return new PreProcessPipeline(new IPreProcessor[]
{
_allProcessors.Value.OfType<JsMinifier>().First(),
_allProcessors.Value.OfType<JsSourceMapProcessor>().First()
});
_allProcessors.Value.OfType<JsMinifier>().FirstOrDefault(),
_allProcessors.Value.OfType<JsSourceMapProcessor>().FirstOrDefault()
}.Where(p => p != null));
case WebFileType.Css:
default:
return new PreProcessPipeline(new IPreProcessor[]
{
_allProcessors.Value.OfType<CssImportProcessor>().First(),
_allProcessors.Value.OfType<CssUrlProcessor>().First(),
_allProcessors.Value.OfType<CssMinifier>().First()
});
_allProcessors.Value.OfType<CssImportProcessor>().FirstOrDefault(),
_allProcessors.Value.OfType<CssUrlProcessor>().FirstOrDefault(),
_allProcessors.Value.OfType<CssMinifier>().FirstOrDefault()
}.Where(p => p != null));
}
});
}
Expand All @@ -93,4 +94,4 @@ public Func<WebFileType, PreProcessPipeline, PreProcessPipeline> OnCreateDefault
set { _setGetDefaultCallback = value; }
}
}
}
}
33 changes: 33 additions & 0 deletions src/Smidge.Core/HostEnvironmentProfileStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.Extensions.Hosting;
using Smidge.Options;

namespace Smidge
{
/// <summary>
/// An implementation of ISmidgeProfileStrategy that will use the host environment to determine if the Debug profile should be used.
/// </summary>
/// <seealso cref="ISmidgeProfileStrategy" />
public class HostEnvironmentProfileStrategy : ISmidgeProfileStrategy
{
private readonly IHostEnvironment _hostEnvironment;


public HostEnvironmentProfileStrategy(IHostEnvironment hostEnvironment)
{
_hostEnvironment = hostEnvironment;
}


private string _profileName;

public string GetCurrentProfileName() => _profileName ??= GetProfileForEnvironment(_hostEnvironment);


protected virtual string GetProfileForEnvironment(IHostEnvironment hostEnvironment)
{
return hostEnvironment.IsDevelopment()
? SmidgeOptionsProfile.Debug
: SmidgeOptionsProfile.Default;
}
}
}
12 changes: 12 additions & 0 deletions src/Smidge.Core/ISmidgeProfileStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

namespace Smidge
{

/// <summary>
/// An interface that returns the name of an options profile to use for the current request.
/// </summary>
public interface ISmidgeProfileStrategy
{
string GetCurrentProfileName();
}
}
22 changes: 21 additions & 1 deletion src/Smidge.Core/Models/Bundle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,30 @@ public Bundle(string name, List<IWebFile> files, BundleEnvironmentOptions bundle
/// </summary>
public Func<IEnumerable<IWebFile>, IEnumerable<IWebFile>> OrderingCallback { get; private set; }



/// <summary>
/// The name of the Profile used to configure the BundleOptions for this bundle.
/// If a BundleOptions has also been specified, the ProfileName will have no effect.
/// </summary>
public string ProfileName { get; set; }

/// <summary>
/// Set the name of the Profile to use for this bundle.
/// </summary>
/// <param name="profileName">Name of the profile.</param>
public Bundle UseProfile(string profileName)
{
ProfileName = profileName;
return this;
}



/// <summary>
/// Defines the options for this bundle
/// </summary>
public BundleEnvironmentOptions BundleOptions { get; private set; }
public BundleEnvironmentOptions BundleOptions { get; private set; }

/// <summary>
/// Sets the options for the bundle
Expand Down
64 changes: 59 additions & 5 deletions src/Smidge.Core/Models/BundleExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Smidge.Options;
using System;
using Smidge.Options;

namespace Smidge.Models
{
Expand All @@ -11,21 +12,42 @@ public static class BundleExtensions
/// <param name="bundleMgr"></param>
/// <param name="debug"></param>
/// <returns></returns>
[Obsolete("Use GetBundleOptions(IBundleManager, string) and specify a configuration profile name.")]
public static BundleOptions GetBundleOptions(this Bundle bundle, IBundleManager bundleMgr, bool debug)
{
var bundleOptions = debug
? (bundle.BundleOptions == null ? bundleMgr.DefaultBundleOptions.DebugOptions : bundle.BundleOptions.DebugOptions)
: (bundle.BundleOptions == null ? bundleMgr.DefaultBundleOptions.ProductionOptions : bundle.BundleOptions.ProductionOptions);
? GetBundleOptions(bundle, bundleMgr, SmidgeOptionsProfile.Debug)
: GetBundleOptions(bundle, bundleMgr, SmidgeOptionsProfile.Default);

return bundleOptions;
}

/// <summary>
/// Get the bundle options from the bundle if they have been set otherwise with the defaults
/// </summary>
/// <param name="bundle"></param>
/// <param name="bundleMgr"></param>
/// <param name="profileName"></param>
/// <returns></returns>
public static BundleOptions GetBundleOptions(this Bundle bundle, IBundleManager bundleMgr, string profileName)
{
var bundleOptions = bundle.BundleOptions == null
? bundleMgr.DefaultBundleOptions[profileName]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to check the profileName exists here with TryGetValue(profileName, out var options) instead of assuming the key exists?

Copy link
Author

@gplwhite gplwhite Dec 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't assume the profile exists. The BundleEnvironmentOptions collection guarantees that a BundleOptions instance will be returned when asking for a BundleOptions instance via the indexer property (a new instance will be created and stored in the collection if a profile with the requested name doesn't yet exist). (See BundleEnvironmentOptions indexer)

In the case you really want to know if a named profile exists (and without automatically creating one if it doesnt) you can call BundleEnvironmentOptions.TryGetProfileOptions();

So, in general it is safe to call bundleMgr.DefaultBundleOptions[profileName] or bundle.BundleOptions[profileName] without checking the return value for null.

: bundle.BundleOptions[profileName];

return bundleOptions;
}




/// <summary>
/// Gets the default bundle options based on whether we're in debug or not
/// </summary>
/// <param name="bundleMgr"></param>
/// <param name="debug"></param>
/// <returns></returns>
[Obsolete("Use GetDefaultBundleOptions(IBundleManager, string) and specify a configuration profile name.")]
public static BundleOptions GetDefaultBundleOptions(this IBundleManager bundleMgr, bool debug)
{
var bundleOptions = debug
Expand All @@ -35,11 +57,43 @@ public static BundleOptions GetDefaultBundleOptions(this IBundleManager bundleMg
return bundleOptions;
}


/// <summary>
/// Gets the default bundle options for a particular configuration profile.
/// </summary>
/// <param name="bundleMgr"></param>
/// <param name="profileName">The name of a configuration profile.</param>
/// <returns></returns>
public static BundleOptions GetDefaultBundleOptions(this IBundleManager bundleMgr, string profileName)
{
var bundleOptions = bundleMgr.DefaultBundleOptions[profileName];

return bundleOptions;
}





[Obsolete("Use GetAvailableOrDefaultBundleOptions(BundleOptions, string) and specify a configuration profile name.")]
public static BundleOptions GetAvailableOrDefaultBundleOptions(this IBundleManager bundleMgr, BundleOptions options, bool debug)
{
return GetAvailableOrDefaultBundleOptions(bundleMgr, options, debug ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default);
}


public static BundleOptions GetAvailableOrDefaultBundleOptions(this IBundleManager bundleMgr, BundleOptions options)
{
return GetAvailableOrDefaultBundleOptions(bundleMgr, options, SmidgeOptionsProfile.Default);
}

public static BundleOptions GetAvailableOrDefaultBundleOptions(this IBundleManager bundleMgr, BundleOptions options, string profileName)
{
return options != null
? options
: bundleMgr.GetDefaultBundleOptions(debug);
: bundleMgr.GetDefaultBundleOptions(profileName);
}


}
}
}
74 changes: 63 additions & 11 deletions src/Smidge.Core/Options/BundleEnvironmentOptions.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using System;
using Smidge.Cache;
using System.Collections.Concurrent;
using System.Collections.Generic;

namespace Smidge.Options
{


/// <summary>
/// Defines the different bundle options for Debug vs Production
/// Defines the different bundle options for various configuration profiles such as Debug or Production
/// </summary>
public sealed class BundleEnvironmentOptions
{
Expand All @@ -20,14 +20,21 @@ public static BundleEnvironmentOptionsBuilder Create()
return new BundleEnvironmentOptionsBuilder(options);
}



private readonly IDictionary<string, BundleOptions> _profileOptions;


/// <summary>
/// Constructor, sets default options
/// </summary>
public BundleEnvironmentOptions()
{
_profileOptions = new ConcurrentDictionary<string, BundleOptions>();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for the ConcurrentDictionary if it isn't being exposed via the _profileOptions? This means that the concurrency methods of ConcurrentDictionary are not used, especially within the indexer method. I would assume we'd use GetOrAdd (etc...) instead of double operation of checking existence and then writing.


DebugOptions = new BundleOptions
{

ProcessAsCompositeFile = false,
CompressResult = false,
CacheControlOptions = new CacheControlOptions
Expand All @@ -36,17 +43,62 @@ public BundleEnvironmentOptions()
CacheControlMaxAge = 0
}
};
ProductionOptions = new BundleOptions();
ProductionOptions = new BundleOptions();
}

/// <summary>
/// The options for the "debug" profile
/// </summary>
public BundleOptions DebugOptions
{
get => this[SmidgeOptionsProfile.Debug];
set => this[SmidgeOptionsProfile.Debug] = value;
}

/// <summary>
/// The options for debug mode
/// The options for "production" profile
/// </summary>
public BundleOptions DebugOptions { get; set; }
public BundleOptions ProductionOptions
{
get => this[SmidgeOptionsProfile.Production];
set => this[SmidgeOptionsProfile.Production] = value;
}



/// <summary>
/// The options for production mode
/// Gets or sets the <see cref="BundleOptions"/> for the specified profile.
/// If the profile has not been previously configured, returns a new <see cref="BundleOptions"/> instance.
/// </summary>
public BundleOptions ProductionOptions { get; set; }
/// <param name="profileName">Name of the profile.</param>
/// <returns></returns>
public BundleOptions this[string profileName]
{
get
{
if (!TryGetProfileOptions(profileName, out BundleOptions options))
{
// Initialise a new BundleOptions for the requested profile
options = new BundleOptions();
_profileOptions.Add(profileName, options);
}

return options;
}
set => _profileOptions[profileName] = value;
}


/// <summary>
/// Gets the <see cref="BundleOptions"/> for the specified profile, if the profile has previously been configured.
/// </summary>
/// <param name="profileName">Name of the profile.</param>
/// <param name="options">The profile options.</param>
/// <returns></returns>
public bool TryGetProfileOptions(string profileName, out BundleOptions options)
{
return _profileOptions.TryGetValue(profileName, out options);
}

}
}
}
Loading