Skip to content

Dervall/Snout

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Snout

  • the fluent interface generator for .NET

NOTE Snout is very much in an alpha state, and if you intend to use it, it's pretty much at your own risk. It's running code right now, but it generates unweildy code and I'm experimenting to see if this stuff can be made context free, because that would be awesome. At least the way you define your grammar will change from what there is now.

What is Snout?

Snout is a tool for making it easier to write and maintain fluent interfaces to your code. A fluent interface is a variety of domain specific language which makes your code read in the manner similar to the english language. Here's an example

var pen = new ShapeBuilder();

pen.AddCircle().WithRadius(10)
   .AddRectangle().Width(10).Height(10)
   .AddPolygon().Point(1,2).Point(2,9).Point(4,8);

These sorts of interfaces are easy to use, but they are very hard to write and a pain to maintain, since introducing new options will invariably result in having to rewrite a lot of interfaces to accomodate a new option. Snout can help you with this!

The Builder pattern

Snout is based on the concept of builders. A builder is a class which is responsible for configuring an object by calling methods in the correct sequence. For instance, in the above example a builder might look like this:

class ShapeBuilder
{
	void AddCircle() { ... }
	void SetCircleRadius(int radius) { ... }

	void AddRectangle() { ... }
	void SetRectangleWidth(int width) { ... }
	void SetRectangleHeight(int height) { ... }

	void AddPolygon() { ... }
	void AddPolygonPoint(int x, int y) { ... }
}

Obviously, this interface is pretty unsafe to use. What if you call AddCircle and then AddPolygonPoint? Bad things. Snout helps you by constructing a layer on top of this to provide a safe and easy syntax.

Using snout

Snout works by reflection and using the generated XML documentations for your assemblies. To generate a fluent interface for the builder that matches the first example, you add documentation to your methods and class. The extra annotations are added in addition to your normal documentation. Your documentation will be transferred to the generated builder code (except for the extra snout specific tags).

An example first, explanations below:

/// <summary>...</summary>
/// <builderclass name="ShapeSyntax" namespace="Snout.Example">
/// (AddCircle WithRadius | AddRectangle Width Height | AddPolygon Point+ ) +
/// </builderclass>
class ShapeBuilder
{
	/// <summary>Add a circle</summary>
	/// <buildermethod/>
	void AddCircle() { ... }

    /// <summary>Set the circle radius</summary>
	/// <param name="radius">Circle radius</param>
	/// <buildermethod name="WithRadius"/>
	void SetCircleRadius(int radius) { ... }

	// Omitted summary and params for brevity on the rest of the 
	// methods.

	/// <buildermethod/>
	void AddRectangle() { ... }

	/// <buildermethod name="Width"/>
	void SetRectangleWidth(int width) { ... }

	/// <buildermethod name="Height"/>
	void SetRectangleHeight(int height) { ... }

	/// <buildermethod/>
	void AddPolygon() { ... }

	/// <buildermethod name="Point"/>
	void AddPolygonPoint(int x, int y) { ... }
}

<builderclass>

builderclass is used to specify a class to generate a DSL from. Apply this to your class.

Attributes

name

Name specifies the name of the generated classes of your fluent interface. The initial class will always be named the exact value of name. Further classes will have derivative names. This name will also be used to name the output file when executing Snout.

namespace

Namespace of the generated classes.

Content

Content of this node is a regular grammar, defined as a regular expression of sorts. Instead of letters you use the symbols that determined by the dslname of the buildermethod tags. If dslname is not specified, use the name attribute. If name isn't specified use the name of the commented methods.

Operators

Similar to regular expressions you can use operators. They act the same way as you'd expect and the priorities are the same as using regular expressions. Supported operators are:

    • Repeat once or more time
  • ? Repeat zero or once
    • Repeat zero or more
  • | Alternation, use either side of the operator.
  • () Parenthesis are used just as with regular expressions. There is no concept of capture groups, just use normal undecorated parenthesis.

Whitespace is ignored. You can use c-style comments.

<buildermethod>

Buildermethod is used to specify that a method is to be available to the generated fluent interface. Apply to method documentation.

Attributes

name

This is the name that your buildermethod will be exposed as in the fluent configuration. This enables you to use different naming terminologies in your builder than in your fluent interface. If this attribute is not specified the name of the method will be used. This needs to be a valid C# method name, and if dslname isn't specified it must be unique within the class scope regardless of parameters.

dslname

Dslname is used to specify a different name for use in the fluent configuration content in the builderclass tag. This becomes neccessary if you intend to overload some of your methods, since you cannot specify parameters while building your fluent grammar. If not set, this will default to the value of the name parameter if specified or, if name is not specified, the name of the method.

useproperty

Normally Snout will generate properties for all methods which have no parameters and have no generic parameters instead of method calls. Set this attribute to false if you do not desire this behaviour.

Running Snout

Snout is a console application. Invoke normally using the command prompt of your choice.

Usage
  -a, --assembly=VALUE       Path to assembly to use
  -d, --doc=VALUE            Path to documentation to use
  -o, --output=VALUE         Output path

Only assembly is required. If doc is not specified it will search in the same folder as the assembly. If output isn't specified Snout will output files in the current directory.

More examples

Snout has been developed primarily as a method of maintaining and developing the fluent interfaces for Piglet, the fluent parser generator. In this function, it has seen a bit of use and viewing the sources of Piglet (more specifically, the RuleBuilder class) is helpful. In fact, it is Piglet which powers Snout, making a fun little circle of life :)

Bug tracker

Please create an issue here on GitHub for any issue you might have. Or just fork it and fix stuff as you wish, that's what open source is for.

https://github.com/Dervall/Snout/issues

Authors

Per Dervall

Copyright and license

Snout is licenced under the MIT license. Refer to LICENSE.txt for more information.

About

A fluent interface code generator

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages