På senare år så har ordet refaktorering (refactor/refactoring) använts felaktigt. Ordet har slängts hit och dit utan att man har brytt sig om dess betydelse. Det har gått så långt att ordet refaktorera inte längre betyder något vettigt, utan bara är ett samlingsnamn för något som har med kodförändring att göra. En kodförändring är inte detsamma som en refaktorering, det kan vara, men inte alltid.
Var är då refaktorisera? Ja, jag tänker skippa William Opdyke original definition och istället citera Martin Fowler:"Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior. Its heart is a series of small behavior preserving transformations. Each transformation (called a 'refactoring') does little, but a sequence of transformations can produce a significant restructuring. Since each refactoring is small, it's less likely to go wrong. The system is also kept fully working after each small refactoring, reducing the chances that a system can get seriously broken during the restructuring."
Alltså, små förändringar i den interna strukturen som inte påverkar det externa beteendet samtidigt som systemet behålls fungerande. Det handlar alltså om vilken avsikt man har med sin förändring.
Exempel från verkligheten:
”Vi har haft en refactorerings-sprint”
Jag ryser av detta, något är fel. Vi skall inte säga att vi refaktoriserar när vi sitter och skriver om vår skräpkod så att den fungerar. Och hur kan detta få bli en sprint? Har vi skapat en hög med skräp så handlar det väl snarare om att städa bort den.
”Vi refaktorerade lite för att lösa en bugg”
Grattis! Ni har skrivit om koden så att den ser finare ut men fortfarande innehåller samma bugg. Om principen för refaktorering är att inte ändra det externa beteendet så kan aldrig en refaktorering lösa en defekt.
”Vi har refactoriserat om databaslagret”
Detta är en klassiker. Det handlar oftast om en omskrivning av ett datalager från en teknik till en annan. Är detta verkligen små förändringar där systemet fortfarande fungerar? Tror inte det.
”Vi måste få en refaktorerings-story för detta”
Det finns något som är sjukt här. Vi får skriva skräpkod utan att be om lov av produktägaren, men när det gäller att skriva bra kod eller städa upp efter oss, då måste vi fråga om lov. Och gärna i form av en story så vi kan få lite extra poäng av den initiala skräpkoden, allt för att elda på vår velocity.
”Refaktorisera om funktionalitet”
Vad betyder detta egentligen? Om man inte kan svara ja på påståendet: ”altering its internal structure without changing its external behavior” så har man använt fel ord.
Om någon behöver brancha för att genomföra en refaktorisering, så handlar det snarare om en omskrivning eller dylikt. För en refaktorisering behöver aldrig en branch. Låt refaktorering stå för de små förändringar som görs i det kontinuerliga utvecklingsarbetet och inget annat.
Kalla det för vad det är, är det en kodförändring så kalla det för det. Vem försöker vi lura?
2010-02-08
Nu får det vara nog, ge tillbaka refaktoreringen sin betydelse
/
Anders Jönsson
|
21.22
0
kommentarer
Länk
Etikett Utveckling
2010-02-05
Härliga mappningar med AutoMapper
Att mappa/transformera data mellan olika objektstrukturer kan kräva en massa tråkig kod som dessutom kräver mycket underhåll. Sådan mappningskod kan man slippa med hjälp av AutoMapper. AutoMapper är, som namnet antyder, ett ramverk för att mappa data mellan olika objekt. Till exempel om man vill platta ut en struktur eller tvärt om. Eller om man bara vill mappa BE-objekt till DTO-objekt.
Man kan själv specificera hur metoder/properties skall mappas (eller om de inte skall mappas) eller så använder man sig av de inbyggda naming convensions som finns.
Exempel:
Här mappar vi från ett platt objekt till ett annat. Metodprefix såsom "Get" mappas automatiskt:
public class ClassFrom
{
public string FirstName { get; protected set; }
public string LastName { get; protected set; }
public string GetName() { return string.Format("{0} {1}", FirstName, LastName); }
public ClassFrom(string firstName, string lastName) { FirstName = firstName; LastName = lastName; }
}
public class ClassTo
{
public string FirstName { get; protected set; }
public string LastName { get; protected set; }
public string Name { get; protected set; }
}
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.CreateMap<ClassFrom, ClassTo>();
Mapper.AssertConfigurationIsValid();
}
}
[TestFixture]
public class AutoMapperSpecifications_When_mapping_from_ClassFrom_to_ClassTo
{
private ClassTo to;
[TestFixtureSetUp]
public void Context()
{
AutoMapperConfiguration.Configure();
ClassFrom from = new ClassFrom("Per", "Persson");
to = Mapper.Map<ClassFrom, ClassTo>(from);
}
[Test]
public void Firstname_should_be_mapped()
{
Assert.That(to.FirstName, Is.EqualTo("Per"));
}
[Test]
public void Lastname_should_be_mapped()
{
Assert.That(to.LastName, Is.EqualTo("Persson"));
}
[Test]
public void Name_should_be_mapped()
{
Assert.That(to.Name, Is.EqualTo("Per Persson"));
}
}
Här mappar vi från ett komplext objekt till ett platt objekt genom att specificera hur det skall mappas:
public class ClassFrom
{
public Person Person { get; protected set; }
public Category Category { get; protected set; }
public ClassFrom(Person person, Category category) { Person = person; Category = category; }
}
public class Person
{
public string FirstName { get; protected set; }
public string LastName { get; protected set; }
public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; }
}
public class Category
{
public string Title { get; protected set; }
public Category(string title) { Title = title; }
}
public class ClassTo
{
public string FirstName { get; protected set; }
public string LastName { get; protected set; }
public string Title { get; protected set; }
}
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.CreateMap<ClassFrom, ClassTo>()
.ForMember(to => to.FirstName, option => option.MapFrom(from => from.Person.FirstName))
.ForMember(to => to.LastName, option => option.MapFrom(from => from.Person.LastName))
.ForMember(to => to.Title, option => option.MapFrom(from => from.Category.Title));
Mapper.AssertConfigurationIsValid();
}
}
[TestFixture]
public class AutoMapperSpecifications_When_mapping_from_ClassFrom_to_ClassTo
{
private ClassTo to;
[TestFixtureSetUp]
public void Context()
{
AutoMapperConfiguration.Configure();
ClassFrom from = new ClassFrom(new Person("Per", "Persson"), new Category("Bloggare"));
to = Mapper.Map<ClassFrom, ClassTo>(from);
}
[Test]
public void Firstname_should_be_mapped()
{
Assert.That(to.FirstName, Is.EqualTo("Per"));
}
[Test]
public void Lastname_should_be_mapped()
{
Assert.That(to.LastName, Is.EqualTo("Persson"));
}
[Test]
public void Title_should_be_mapped()
{
Assert.That(to.Title, Is.EqualTo("Bloggare"));
}
}
Här mappar vi från ett komplext objekt till ett platt objekt genom att använda AutoMappers naming convention (Person.FirstName blir PersonFirstName)
public class ClassFrom
{
public Person Person { get; protected set; }
public Category Category { get; protected set; }
public ClassFrom(Person person, Category category) { Person = person; Category = category; }
}
public class Person
{
public string FirstName { get; protected set; }
public string LastName { get; protected set; }
public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; }
}
public class Category
{
public string Title { get; protected set; }
public Category(string title) { Title = title; }
}
public class ClassTo
{
public string PersonFirstName { get; protected set; }
public string PersonLastName { get; protected set; }
public string CategoryTitle { get; protected set; }
}
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.CreateMap<ClassFrom, ClassTo>();
Mapper.AssertConfigurationIsValid();
}
}
[TestFixture]
public class AutoMapperSpecifications_When_mapping_from_ClassFrom_to_ClassTo
{
private ClassTo to;
[TestFixtureSetUp]
public void Context()
{
AutoMapperConfiguration.Configure();
ClassFrom from = new ClassFrom(new Person("Per", "Persson"), new Category("Bloggare"));
to = Mapper.Map<ClassFrom, ClassTo>(from);
}
[Test]
public void Firstname_should_be_mapped()
{
Assert.That(to.PersonFirstName, Is.EqualTo("Per"));
}
[Test]
public void Lastname_should_be_mapped()
{
Assert.That(to.PersonLastName, Is.EqualTo("Persson"));
}
[Test]
public void Title_should_be_mapped()
{
Assert.That(to.CategoryTitle, Is.EqualTo("Bloggare"));
}
}
Här mappar vi från ett platt objekt till ett komplext object
public class ClassFrom
{
public string FirstName { get; protected set; }
public string LastName { get; protected set; }
public string Title { get; protected set; }
public ClassFrom(string firstName, string lastName, string title)
{
FirstName = firstName;
LastName = lastName;
Title = title;
}
}
public class Person
{
public string FirstName { get; protected set; }
public string LastName { get; protected set; }
}
public class Category
{
public string Title { get; protected set; }
}
public class ClassTo
{
public Person Person { get; protected set; }
public Category Category { get; protected set; }
}
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.CreateMap<ClassFrom, Person>();
Mapper.CreateMap<ClassFrom, Category>();
Mapper.CreateMap<ClassFrom, ClassTo>()
.ForMember(to => to.Person, option => option.MapFrom(from => from))
.ForMember(to => to.Category, option => option.MapFrom(from => from));
Mapper.AssertConfigurationIsValid();
}
}
[TestFixture]
public class AutoMapperSpecifications_When_mapping_from_ClassFrom_to_ClassTo
{
private ClassTo to;
[TestFixtureSetUp]
public void Context()
{
AutoMapperConfiguration.Configure();
ClassFrom from = new ClassFrom("Per", "Persson", "Bloggare");
to = Mapper.Map<ClassFrom, ClassTo>(from);
}
[Test]
public void Person_firstname_should_be_mapped()
{
Assert.That(to.Person.FirstName, Is.EqualTo("Per"));
}
[Test]
public void Person_lastname_should_be_mapped()
{
Assert.That(to.Person.LastName, Is.EqualTo("Persson"));
}
[Test]
public void Category_title_should_be_mapped()
{
Assert.That(to.Category.Title, Is.EqualTo("Bloggare"));
}
}
Här mappar vi från ett platt objekt till ett objekt med Value Objects
public class ClassFrom
{
public string FirstName { get; protected set; }
public string LastName { get; protected set; }
public ClassFrom(string firstName, string lastName) { FirstName = firstName; LastName = lastName; }
}
public class ClassTo
{
public FirstName FirstName { get; protected set; }
public LastName LastName { get; protected set; }
}
public class FirstName
{
protected string firstName;
public FirstName(string firstName) { this.firstName = firstName; }
public string GetValue() { return firstName; }
}
public class LastName
{
protected string lastName;
public LastName(string lastName) { this.lastName = lastName; }
public string GetValue() { return lastName; }
}
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.CreateMap<string, LastName>()
.ConstructUsing(lastName => new LastName(lastName));
Mapper.CreateMap<string, FirstName>()
.ConstructUsing(firstName => new FirstName(firstName));
Mapper.CreateMap<ClassFrom, ClassTo>();
Mapper.AssertConfigurationIsValid();
}
}
[TestFixture]
public class AutoMapperSpecifications_When_mapping_from_ClassFrom_to_ClassTo
{
private ClassTo to;
[TestFixtureSetUp]
public void Context()
{
AutoMapperConfiguration.Configure();
ClassFrom from = new ClassFrom("Per", "Persson");
to = Mapper.Map<ClassFrom, ClassTo>(from);
}
[Test]
public void Firstname_should_be_mapped()
{
Assert.That(to.FirstName.GetValue(), Is.EqualTo("Per"));
}
[Test]
public void Lastname_should_be_mapped()
{
Assert.That(to.LastName.GetValue(), Is.EqualTo("Persson"));
}
}
Detta var bara några sätt man kan använda AutoMapper på, för mer exempel och dokumentation, se: http://automapper.codeplex.com/.
/
Anders Jönsson
|
12.15
0
kommentarer
Länk
Etikett Alt.Net, Arkitektur och Design, C#, Utveckling
2010-01-04
jQuery och ASP.NET-kontroller
När man använder jQuery tillsammans med ASP.NET kan man få lite problem med att få tag i de kontroller man har skapat. Detta eftersom att kontrollernas ID i "runtime" inte är exakt lika med vad man har satt de till. Till exempel:
Om du har en kontroll som ligger i en master page så kommer:
<asp:TextBox ID="PersonAge" runat="server"></asp:TextBox>
I runtime att bli:
<input name="ctl00$EditArea$PersonAge" type="text" id="ctl00_EditArea_PersonAge" />
Vilket gör att om du har ett jQuery som går direkt på ID't "PersonAge":
var text = $("#PersonAge").val();
alert(text);
Så kommer detta inte att fungera. (Det kommer ge "undefined")
Det finns olika sätt att lösa det på. Om du kör .Net 4.0 så kan du sätta vilket ID kontrollen skall ha vid runtime - då är problement löst.
Om man har möjlighet att använda sig av ASP.NET-kontrollens CLientID så kan man lätt få fram vad den kommer att ha för id i runtime. Tex:
var text = $("#<% = PersonAge.ClientID %>").val();
alert(text);
Eftersom all javascript-kod bör ligga i separata filer och inte vara inline så blir det lite bökigare eftersom att ClientID inte går att anropa lika enkelt. Det finns olika sätt att lösa detta på. Ett sätt är att ha en variabel inline på sidan som sparar undan ClientID för att sedan använda denna variabeln för att få tag på kontrollen.
Ett annat sätt är att att använda sig av regex för att få tag på kontrollen. Med "$" berättar jag att id't slutar på "PersonAge" och därmed spelar det ingen roll vad som står innan. Du kan även använda "^" för att säga att någonting börjar med en viss text. Detta sätt är dock inte så bra eftersom det är slött.
var text = $("input[id$='PersonAge']").val();
alert(text);
Ett annat alternativ är att lägga till en css-klass som identifierar kontrollen. Denna klass behöver inte finnas på riktigt (den kommer inte slöa ner renderingen) utan används bara för att få tag på kontrollen. Tex om vi lägger till css-klassen "PersonAge" så kan jag enkelt få tag på kontrollen.
<asp:TextBox ID="PersonAge" runat="server" CssClass="PersonAge"></asp:TextBox>
var text = $("input.PersonAge").val();
alert(text);
Vilket sätt du skall välja beror givetvis på det enskilda scenariot.
/
Anders Jönsson
|
23.51
0
kommentarer
Länk
2009-12-08
Läsbara metodanrop
Ibland kan koden bli lite mer läsbar som man skippar återupprepningar. Det vill säga i stället för att skriva
repository.GetProductByNumber(number)
kan man skriva
repository.GetProductBy(number)
eller
grid.LoadResourceForUser(user)
blir
grid.LoadResourceFor(user)
Eller vad säger ni?
/
Anders Jönsson
|
20.40
5
kommentarer
Länk
2009-11-29
Valmöjligheter är inte komplexitet
På senare tid har det varit mycket diskussion om ”Is software development too complex?”. Vissa menar att mjukvaruutveckling är för komplext i och med att det finns så många olika tekniker, ramverk, leverantörer med mera att välja mellan.
Min syn är dock att valmöjligheter inte relaterar till komplexitet. Att ha många olika val ger oss bättre chans till att få mjukvaruutveckling till att bli mindre komplex snarare än tvärtom. Att ha flera val ger oss möjligheten till att skapa en bättre process där vi hittar val som är nischade till att lösa just våra problem. Genom att vi kan plocka ihop en verktygslåda med enbart de verktyg som passar oss kan vi skapa mindre komplexa lösningar samtidig som att vår process kan bli mindre komplex. Hade vi inte haft många val så hade vi varit tvungna att välja verktyg som egentligen inte passar oss och därmed behövt skapa en mer komplex process. Till exempel om man måste välja ett ramverk som egentligen inte passar kommer man behöva skriva en massa onödig kod för att få det att fungera som man vill och därigenom introduceras onödig komplexitet.
Att vi börjar få många val i .Net-världen är något som vi skall njuta av – inte arbeta emot eller gnälla över. Om man tycker det är jobbigt att behöva tänka själv över vilka val man skall göra – ja, då är nog inte utvecklingsbranschen något för en.
/
Anders Jönsson
|
21.55
3
kommentarer
Länk
Etikett Arkitektur och Design, Utveckling
2009-11-23
Podcast
För några dagar sedan fick jag frågan om jag hade några tips på bra podcast, antigen bra serier eller bara enstaka avsnitt.
Nedan är några podcast-serier som jag följer samt några avsnitt som jag tycker har varit extra intressanta. Det är ganska ojämn kvalité på avsnitten, vissa är bra medan andra inte är så bra.
ALT.NET (ej aktiv), http://feeds.feedburner.com/altnetpodcast
"18: Talking with Jeremy Miller about Alt.Net"
"15: Domain Driven Design"
Elegant Code, http://feeds.feedburner.com/elegantcodecast
"Code Cast 30 – Story Teller with Jeremy Miller"
Software Engineering Radio, http://se-radio.net/rss
"Episode 139: Fearless Change with Linda Rising"
"Episode 138: Learning as a Part of Development with Allan Kelly"
Hansel Minutes, http://feeds.feedburner.com/HanselminutesCompleteMP3
"The History and Future of Web Standards with Molly Holzschlag"
"SOLID Principles with Uncle Bob"
Hearding Code, http://herdingcode.com/?feed=rss2
"Herding Code 58: Presentation Patterns with Jeremy Miller, Ward Bell, Rob Eisenberg and Glenn Block (Part 1) "
"Herding Code 58: Presentation Patterns with Jeremy Miller, Ward Bell, Rob Eisenberg and Glenn Block (Part 2)"
.NET Rocks!, http://feeds.feedburner.com/dotnetrocks_AAC
Polymorphiv Podcast, http://polymorphicpodcast.com/podcast/feed/
Ruby on Rails Podcast, http://feeds.feedburner.com/rubyonrailspodcast
Har du tips på någon bra?
/
Anders Jönsson
|
12.50
1 kommentarer
Länk
Etikett Utveckling
2009-11-09
Testa genom arv
Om man befinner sig i situationen att man behöver skriva test för protected metoder så är det oftast något annat som är det egentliga problemet. Kanske behöver man bryta ut något koncept till en ny klass eller bara tänka om i hur man har designat/kodat. Det finns dock scenarion där det finns en nytta med att kunna testa genom arv, därav är det viktigt att kunna detta som ett verktyg i sin TDD-låda.
Att testa kod via arv är enkelt, det enda man behöver göra är att ärva från den klassen man vill testa och sedan göra en publik metod i sin ärvda klass som publicerar den dolda metoden. Exempel:
I koden nedan jagar vi en bugg som vi tror finns i metoden IsValidH2O och vi skulle således behöva se i vårt specifikation (test) om den returnerar sant eller falskt. Vi ärver från klassen och skriver en metod (IsValidH2O_ForSpec) som publicerar IsValidH2O. Det är denna metod vi testar emot.
public class TheHolyGrailSpecs_When_the_h2o_is_holy
{
private TheHolyGrail_ForSpec theHolyGrail;
[SetUp]
public void Context()
{
IH20 h2oStub = new H20Stub();
theHolyGrail = new TheHolyGrail_ForSpec(h2oStub);
}
[Test]
public void It_should_be_classified_as_valid_h2o()
{
Assert.That(theHolyGrail.IsValidH2O_ForSpec(), Is.True);
}
}
public class TheHolyGrail : IGrail
{
private readonly IH20 h2o;
public TheHolyGrail(IH20 h2o)
{
this.h2o = h2o;
}
protected bool IsValidH2O()
{
// Code
return true;
}
}
public class TheHolyGrail_ForSpec : TheHolyGrail
{
public TheHolyGrail_ForSpec(IH20 h2o) : base(h2o)
{
}
public bool IsValidH2O_ForSpec()
{
return base.IsValidH2O();
}
}
Enkelt men kraftfullt. Dock är detta inget sätt man skall använda ofta eftersom testen blir sköra och fula. Men ibland finns det tillfällen då de kan hjälpa en.
/
Anders Jönsson
|
13.00
1 kommentarer
Länk
Etikett Test, Utveckling
2009-10-29
JavaZone 2009
Av en slump snubblade jag över en radda med inspelningar från JavaZone 2009 (sept 9-10). Jag blev förvånad över hur många sessioner som verkade intressanta. Eller vad sägs om följande:
- The Productive Programmer: Mechanics - Neal Ford
- The Uncertainty Principle - Kevlin Henney
- Code Smells and Refactoring Revisited: Advances from the software engineering research community - Aiko Fallas Yamashita
- Lizard Brain Web Design - Scott Davis
- One web, CSS3, and HTML5 - Håkon Wium Lie
- DDD panel - Kevlin Henney, Eric Evans, Einar Landre, Scott Davis, Phil Wills, Greg Young, Randy Stafford
- Strategic Design & Responsibility Traps - Eric Evans
- Patterns for persisting large and rich domain models - Randy Stafford
/
Anders Jönsson
|
22.24
2
kommentarer
Länk
Etikett Alt.Net, Arkitektur och Design, Utveckling
2009-10-25
AssertWasCalled och Assert.Throws
Ett tips när man skriver specifikationer (test) med Rhino Mock och NUnit är att använda AssertWasCalled och Assert.Throws istället för andra mindre tydliga sätt.
AssertWasCalled
För att specificera att en viss metod skall anropas kan man använda AssertWasCalled med tillhörande MethodOptions:
ITheDependency theDependency = MockRepository.GenerateMock<ITheDependency>();
ITheClass theClass = new TheClass(theDependency);
theClass.MethodX();
theDependency.AssertWasCalled(x => x.MethodZ(null),
callOptions => {
callOptions.Repeat.Once();
callOptions.IgnoreArguments();
});
Assert.Throws
För att specificera att ett visst beteende skall kasta ett fel, kan man använda Assert.Throws istället för ExpectedException / try.../catch:
ITheDependency theDependency = MockRepository.GenerateMock<ITheDependency>();
ITheClass theClass = new TheClass(theDependency);
Assert.Throws<ArgumentNullException>(() => theClass.MethodY(null));
Testklasser:
Läs mer på: http://nunit.com/index.php?p=assertThrows&r=2.5 och http://www.ayende.com/wiki/Rhino+Mocks+3.5.ashx
public interface ITheDependency
{
void MethodZ(string value);
}
public interface ITheClass
{
void MethodX();
void MethodY(string value);
}
public class TheClass : ITheClass
{
private readonly ITheDependency _theDependency;
public TheClass(ITheDependency theDependency)
{
_theDependency = theDependency;
}
public void MethodX()
{
// Do Stuff
_theDependency.MethodZ("value");
}
public void MethodY(string value)
{
if (value == null)
{
throw new ArgumentNullException();
}
// Do Stuff
}
}
/
Anders Jönsson
|
14.10
2
kommentarer
Länk
Etikett Specifikationer, Test, Utveckling
2009-10-18
Minimera storleken på dina JavaScript med Microsoft Ajax Minifier och MSBuild
I samband med att Microsoft Ajax Library (Preview 6) annonserades i början av veckan så annonserades även ett nytt verktyg för webbutvecklare, Microsoft Ajax Minifier. Med Microsoft Ajax Minifier kan man minska storleken på de JavaScript-filer som läggs i produktion. Genom att ta bort mellanslag, kommentarer, radbryt, döpa om lokala variabler så kan storleken minska drastiskt.
Enligt en jämförelse som Scott Guthrie har gjort så minimeras filerna rejält med Minifier:
(Referens)
Verktyget för detta finns i tre olika utföranden:
ajaxmin.exe - för kommandoprompten
ajaxmintask.dll – en task för MSBuild skript
ajaxmin.dll – en dll som kan användas från kod
I exemplet nedan kommer jag att använda MSBuild för att minimera de skript som finns på en webbplats.
1. Börja med att installera Microsoft Ajax Minifier
2. Skapa ett MSBuild skript (ex "jsMinifier.msbuild") med följande xml:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Run">
<UsingTask TaskName="AjaxMin" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\MicrosoftAjax\AjaxMinTask.dll" />
<PropertyGroup>
<SearchPath>$(MSBuildStartupDirectory)\**\*</SearchPath>
<SearchInclude>.js</SearchInclude>
<SearchExclude>.min.js</SearchExclude>
</PropertyGroup>
<Target Name="Run">
<CreateItem Include="$(SearchPath)$(SearchInclude)" Exclude="$(SearchPath)$(SearchExclude)">
<Output TaskParameter="Include" ItemName="JavaScriptFiles"/>
</CreateItem>
<AjaxMin
SourceFiles="@(JavaScriptFiles)"
SourceExtensionPattern="\$(SearchInclude)$"
TargetExtension="$(SearchExclude)"/>
<AjaxMin SourceFiles="@(JavaScriptFiles)"
SourceExtensionPattern="\$(SearchInclude)$"
TargetExtension=".hard$(SearchExclude)"
CollapseToLiteral="True"
LocalRenaming="CrunchAll"
OutputMode="SingleLine"
RemoveUnneededCode="True"
StripDebugStatements="True"
EvalsAreSafe="True"
InlineSafeSettings="True"
CombineDuplicateLiterals="True"
CatchAsLocal="True"/>
</Target>
</Project>
Den plats som skriptet körs ifrån kommer agera som root för sökningen efter *.js-filer. Det vill säga om skriptet ligger i ...\WebSite\ så kommer alla undermappar till "WebSite" att sökas igenom.
Skriptet kör AjaxMin på två olika sätt, ett normalläge (nedan märkt med "min") och ett så kallat "Hypercrunching"-läge (nedan märkt med "hard"). När alla attribut är påslagna kommer skripten att minimeras mycket.
Exempel:
MSBuild-skriptet kan initieras från olika håll; Visual Studio, byggserver, bat-fil med mera. För att köra den från en bat-fil är det bara att skriva "%WinDir%\Microsoft.NET\Framework\v3.5\MSBuild.exe jsMinifier.msbuild".
Mer om vad de olika egenskaperna gör och hur man kan använda detta från Visual Studio går att läsa i dokumentationen som följer med i installationen.
Läs mer på: http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=34488
/
Anders Jönsson
|
20.33
1 kommentarer
Länk
Etikett Nyttoprogram, Utveckling, Web
RSS
Atom