Skip to content

Instantly share code, notes, and snippets.

@trayburn
Last active December 17, 2015 05:29
Show Gist options
  • Save trayburn/5558458 to your computer and use it in GitHub Desktop.
Save trayburn/5558458 to your computer and use it in GitHub Desktop.
Powershell Scaffolding Example

PowerShell Scaffolding

Amir's RRYN Version (RRYN == Ruby Rake Yaml Nokogiri)

Setup

First, assuming you've not done a bunch of PowerShell before, lets get you setup with the minimums (in my opinion)

  • Set your execution policy, if you haven't already. You can choose Unrestricted if you prefer, RemoteSigned is a minimum for PSGet.
Set-ExecutionPolicy RemoteSigned
(new-object Net.WebClient).DownloadString("http://psget.net/GetPsGet.ps1") | iex
  • Install PSake (pronounced Sake, like the rice-wine)
install-module psake
import-module psake
  • Personally I setup an alias for PSake
New-Alias psake Invoke-psake

PSake Commands

Run the model task, add a Person (which I made a default) :

psake model

Run the model task, add an AwesomeClass :

psake Model -properties @{name="AwesomeClass"}

Document all tasks:

psake .\default.ps1 -docs
@{
website_port = 80
website_deploy_directory = 'C:\inetpub\website'
solution_name = 'ASolution'
mvc_project = 'AProject'
database = '(local)\SQLEXPRESS'
}
Include ".\utilities.ps1"
properties {
$config = &".\config.ps1"
$config | %{ Set-Variable -Scope 1 -Name $_ -Value $config[$_] }
$mvc_project_directory = $config.mvc_project
# Provide a default (could be done in config.ps1)
# which can be overriden at command line by -Properties argument.
$name = "Person"
}
task model -depends setup `
-description "adds a dynamic model class to your mvc project" `
{
#error validation, did they pass the name argument?
Assert ($name -ne $null) "name parameter required, usage: Invoke-Psake model @{`$n=`"Person`"}"
#create the template, if it doesn't exist already
$path = "$mvc_project_directory\Models\$name.cs"
Assert ((Test-Path $path) -eq $false) "File already exists"
New-Model | Set-Content $path
#add class to cs project
Insert-CompileNode Models $name
}
task clean {
Remove-Item $mvc_project_directory -Recurse -Force -ErrorAction SilentlyContinue
}
task setup -depends clean { .{
mkdir ".\$mvc_project_directory"
mkdir ".\$mvc_project_directory\Models"
cp ".\TestProject.csproj" "$mvc_project_directory\$mvc_project.csproj"
} | Out-Null}
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5FDF5393-022B-4F00-BF60-34A9A0D806DC}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ConsoleApplication1</RootNamespace>
<AssemblyName>ConsoleApplication1</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Castle.Core">
<HintPath>..\packages\Castle.Core.3.2.0\lib\net45\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Castle.Facilities.Logging">
<HintPath>..\packages\Castle.LoggingFacility.3.2.0\lib\net45\Castle.Facilities.Logging.dll</HintPath>
</Reference>
<Reference Include="Castle.Services.Logging.NLogIntegration">
<HintPath>..\packages\Castle.Core-NLog.3.2.0\lib\net40\Castle.Services.Logging.NLogIntegration.dll</HintPath>
</Reference>
<Reference Include="Castle.Windsor">
<HintPath>..\packages\Castle.Windsor.3.2.0\lib\net45\Castle.Windsor.dll</HintPath>
</Reference>
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.0.1.2\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Topshelf">
<HintPath>..\packages\Topshelf.3.1.1\lib\net40-full\Topshelf.dll</HintPath>
</Reference>
<Reference Include="Topshelf.NLog">
<HintPath>..\packages\Topshelf.NLog.3.1.1\lib\net40-full\Topshelf.NLog.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="bin\Debug\ConsoleApplication1.exe.config" />
<None Include="bin\Debug\ConsoleApplication1.vshost.exe.config" />
<None Include="bin\Debug\ConsoleApplication1.vshost.exe.manifest" />
<None Include="bin\Debug\NLog.config" />
<None Include="bin\Debug\Templates.Service.exe.config" />
<None Include="NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="bin\Debug\ConsoleApplication1.vshost.exe" />
</ItemGroup>
<ItemGroup>
<Folder Include="bin\Release\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
function Insert-CompileNode {
param($dir, $file)
$project = (gi ".\$mvc_project_directory\$mvc_project_directory.csproj").FullName
$node = "<Compile Include='$dir\$file.cs' />"
$doc = ([xml](Get-Content $project))
$doc | `
Select-Xml "//x:ItemGroup[x:Compile]" -Namespace @{"x"=$doc.Project.xmlns} | `
Select-Object -ExpandProperty Node -First 1 | `
% { $_.InnerXml += $node; $doc.Save($project) }
}
function New-Model {
@"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Oak;
namespace $mvc_project_directory.Models
{
public class $name : DynamicModel
{
public $name(object dto) : base(dto) { }
public $name() { }
//IEnumerable<dynamic> Validates() { }
//IEnumerable<dynamic> Associates() { }
}
}
"@
}
@amirrajan
Copy link

@coridrew
Copy link

With the "assuming you've not done a bunch of PowerShell before" caveat, I think adding a few more sentences to the readme could help:

  1. PowerShell is not case sensitive. Varying psake invocation between Model & model calls the same model task (defined in default.ps1).
  2. PSGet, inspired by NuGet, is like Ruby Gems (or NuGet) for PowerShell http://blog.chaliy.name/post/4684130282/psurl-psget-intro
  3. PSake is a build automation tool (like Rake or MSBuild or make) written in PowerShell http://ayende.com/blog/4156/on-psake

Additional notes / clarifying questions:

a. Instead of "Run the model task, add a Person (which I made a default)," I think it would have been more helpful to have read, "Running the model task with no params adds a Person by default (see Default.ps1 properties, below)"

b. Why does execution policy need to be set in PowerShell but not in ruby? Is ruby innately less safe, or is PowerShell innately more dangerous?

@amirrajan
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment