GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing github.com — Script Libraries Script Libraries
Glenn Block edited this page
One of the key features of scripts today is that it allows you to author [Script Packs] (https://github.com/scriptcs/scriptcs/wiki/Script-Packs). These are reusable bits of functionality distributed via NuGet that generally are used to wrap existing libraries and frameworks and make them easier to consume from script. Script Packs offer the ability to inject namespaces and references into the consuming script dynamically. They can also offer a more script-friendly API that automatically handles redundant plumbing code which is generally handled by an IDE. The biggest downside of Script Packs is the authoring. Script Packs today are .NET assemblies, this means you have to open an IDE, create a C# project, and then implement a bunch of interfaces in order to create your pack. This adds significant complexity as compared to the simplicity of writing a scriptcs "app" i.e. a set of csx scripts and a packages.config. It feels against the spirit of scriptcs in that sense. If the experience is greatly simplified, we believe this will pave the way for the scriptcs ecosystem to really blossom. Offer an experience for creating reusable Script Libraries. That is APIs as scripts which can be published as packages and consumed in scriptcs similar to any other NuGet package.
Priya wants to implement a Calculator library. It offers functions like Add, Subtract, Multiply and Divide. She authors the following script: //CalculatorMain.csx public double Add(double a, double b) { ... } public double Subtract(double a, double b) { ... } public double Multiple(double a, double b) { ... } public double Divide(double a, double b) { ... } She tests out her script by using #load in the REPL and it appears to work fine. Now she is ready to package up her script so it can be shared. She creates the following simple folder structure. Next she creates ScriptCs.Calculator.nuspec for her package specifying the required package details, i.e. name: Using Now that the package is ready to test, she goes to a new folder to create her test app. First she installs the package. Next she creates a //no require necessary for this, it is present. var calc = new Calculator(); var result = calc.Add(5,2); Console.WriteLine(result); Priya runs the "app" and is happy to see it compiles successfully and runs. Priya realizes that she add some logging to her calculator: She goes to her nuspec and adds a reference to a the Logger script pack, Logger logger = Require<Logger>(); Note: Priya then adds a bunch of log entries throughout her code. Next she creates a new package and puts it into her local folder which she installed from before. For example this is the Add method: public double Add(double a, double b) { logger.Info(String.Format("Adding {0} + {1}", a, b)); return a+b; } She goes back to her app folder and does Next she reruns her test app and is happy to see the log entries showing up. Script Libraries are normal scriptcs csx files, they support #load, #r, custom directives and require. One key constraint, it must have an "entry" level script which uses the convention [Name]Main.csx i.e. CalculatorMain.csx. This is the main entry point for the script pack. Script Libraries are packaged up as NuGet packages. scripts must be placed in the Content/scriptcs folder within the package. All files must be added explicitly to the nuspec. Any package dependencies should be added as for a normal package. The dependencies can be standard packages, packages containing traditional script packs, or other script based packages. Below is an example NuSpec for the Caclulator library. It contains 2 csx files as well as a dependency on the ScriptCs.Logger.ScriptPack package which is a standard script pack. <?xml version="1.0"?> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>ScriptCs.Calculator</id> <version>0.1.0</version> <authors>Glenn Block</authors> <owners>Glenn Block</owners> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>A calculator script pack</description> <language>en-US</language> <dependencies> <dependency id="ScriptCs.Logger.ScriptPack" version="0.1.0" /> </dependencies> </metadata> <files> <file src="CalculatorMain.csx" target="Content\scriptcs\CalculatorMain.csx" /> </files> </package> Script Libraries will be loaded in two phases at runtime.
This model provides several benefits:
One key constraint this introduces is that binary script packs cannot depend on Script Libraries. This might be able to be addressed in the future but this is TBD. One key challenge with this approach is it could make naming collisions likely. Also there is an issue of scoping. In the .NET proper world, namespaces would be a way to achieve this, but in Roslyn this is not a possibility as you can't have namespaces in script as they execute within an outer class. To remove collisions and introduce some form of containment, we will use an outer class. For each script library, all of it's contents will automatically be wrapped in an outer class. The name for the class will be the name of the Main csx file minus "Main". Using the Calculator example the code will be embedded in a Calculator class. public class Calculator : ScriptLibraryWrapper { public double Add(double a, double b) { ... } public double Subtract(double a, double b) { ... } public double Multiple(double a, double b) { ... } public double Divide(double a, double b) { ... } }
A challenge posed by this model of containment is around usings and directives. Currently in scriptcs we expect all usings and directives to be at the top of the file when we parse it (using the processor). As we are wrapping the entire contents of the library, this can pose a problem as the scripts will likely have usings and directives which will be contained within which will cause the file processor to fail. The way this will be addressed is to walk through all package scripts and pass them through the pre-processor. As each script is processed it will be appended into a script in memory, while all the usings are saved. After all files are processed then the usings will be appended at the top of the file. All ScriptLibrary functionality is implemented in the This file is automatically loaded and its contents are appended to the end of the executing script which in this case would be written in F#. If the default append behavior is not acceptable for that language, then a custom
Clone this wiki locally |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |