<commit_before>##Why Oak?

Oak is about getting things done. It's about maintaining a rapid development cycle. It's about having an immediate and addictive feedback loop in place to keep you in the zone. It's about getting developers on board and getting them up and running fast so that they can start contributing. It's about reducing all aspects of development friction.

##How Oak?
Oak does this by leveraging dynamic constructs in C# 4.0. These constructs make testing frictionless.  Oak maintains a rapid development cycle by making testing easy and by providing an immediate feedack loop using Growl For Windows.  Oak takes advantage of dynamic C# 4.0 and metaprogramming for your data access.  Initial schema creation, seeding sample data, and regenerating database for other developers is provided directly through controller actions that can be leveraged by tests and rake scripts.  Oak is built on top of ASP.NET MVC, so you don't have to re-learn anything, just build upon what you already know.

##What Oak?
- MIT License
- Head over to the [wiki](https://github.com/amirrajan/oak/wiki) for additional details.
- [Reference Implementations](https://github.com/amirrajan/Oak/tree/master/Sample%20Apps) are included, test driven, awesome 
- Built ground up [under test](https://github.com/amirrajan/Oak/tree/master/Oak.Tests)
- Available via Nuget: install-package oak or install-package oak-edge

###Continous testing provided by [NSpec](http://nspec.org) and [SpecWatchr (screen cast)](http://nspec.org/continuoustesting)
**install-package nspec and install-package specwatchr**

Here is a NSpec test for one of the sample apps:

    //tests shows user registration
    class describe_AccountController : _borrowed_games
    {
        AccountController controller;
        dynamic user;
        bool authenticated;

        void before_each()
        {
            controller = new AccountController();
            MockSession(controller);
            controller.Authenticate = s => authenticated = true;
        }

        void registering_for_site()
        {
            context["requesting registration page"] = () =>
            {
                act = () => result = controller.Register();
                it["returns view"] = () => (result as object).should_cast_to<ViewResult>();
            };

            context["user registers"] = () =>
            {
                act = () => result = controller.Register(new
                {
                    Email = "<EMAIL>",
                    Password = "password",
                    PasswordConfirmation = "password"
                });

                it["logs in user"] = () => (result.Url as string).should_be("/");
                it["authenticates user"] = () => authenticated.should_be_true();
                it["sets user in session"] = () => ((decimal)controller.CurrentUser).should_be((decimal)user);

                context["user name is taken"] = () =>
                {
                    before = () => GivenUser("<EMAIL>");
                    it["return error stating that user name is taken"] = () =>
                        (result.ViewBag.Flash as string).should_be("Email is unavailable.");
                };
            };

            context["registration is invalid"] = () =>
            {
                act = () => result = controller.Register(new { Email = default(string), Password = default(string) });
                it["returns error stating that email is required."] = () => 
                    (result.ViewBag.Flash as string).should_be("Email is required.");
            };
        }
    }

###A Rails inspired implementation of ActiveModel called DynamicModel

Here is a "complex" model declaration in Oak:

    public class User : DynamicModel
    {
        Users users = new Users();
        FriendAssociations friendAssociations = new FriendAssociations();
        Games games = new Games();
        Library library = new Library();
        NotInterestedGames notInterestedGames = new NotInterestedGames();
        GameRequests gameRequests = new GameRequests();

        public User(dynamic dto)
        {
            Init(dto);
        }

        //Relationships of User
        public IEnumerable<dynamic> Associates()
        {
            yield return
            new HasManyThrough(users, through: friendAssociations, named: "Friends") { ForeignKey = "IsFollowing" };

            yield return
            new HasManyThrough(games, through: library);

            yield return
            new HasMany(library);

            yield return
            new HasMany(notInterestedGames);

            yield return
            new HasMany(gameRequests);

            yield return
            new HasMany(friendAssociations);
        }

        //User Validation
        public IEnumerable<dynamic> Validates()
        {
            yield return
            new Format("Handle") { With = "^@", ErrorMessage = "Your handle has to start with an '@' sign." };

            yield return
            new Format("Handle") { With = "^[\\S]*$", ErrorMessage = "Your handle can't contain any spaces." };

            yield return
            new Exclusion("Handle") { In = new[] { "@nameless" }, ErrorMessage = "You did not specify a handle." };

            yield return
            new Format("Handle") { With = "[a-zA-Z0-9]+", ErrorMessage = "You did not specify a handle." };

            yield return
            new Format("Handle") { With = "^@[a-zA-Z0-9]*$", ErrorMessage = "Your handle can only be alpha numeric." };

            yield return
            new Uniqueness("Handle", users) { ErrorMessage = "The handle is already taken." };  //awesome
        }

        //Complex Query Made Easy
        public IEnumerable<dynamic> PreferredGames()
        {
            //inner join is handled for you
            var gamesForFriends = This().Friends().Games() as IEnumerable<dynamic>;  //awesome

            var distinctPreferredGames =
                gamesForFriends
                    .Where(s => !OwnsGame(s.Id) && PrefersGame(s.Id) && SharesConsole(s.Console))
                    .Select(game => new
                    {
                        game.Id,
                        game.Name,
                        game.Console,
                        Requested = HasGameBeenRequested(game.Id),
                        Owner = game.User().Select("Id", "Handle")
                    })
                    .OrderBy(s => s.Requested ? 0 : 1)
                    .ThenBy(s => s.Name);

            return distinctPreferredGames;
        }
       
        //who needs extension methods?
        public bool HasGame(dynamic game)
        {
            return This().Games().Any(new { Id = game.Id });  //awesome
        }

        public bool HasGames()
        {
            return This().Games().Any();  //awesome
        }

        public bool HasFriends()
        {
            return This().Friends().Any();  //awesome
        }

        public bool HasGameBeenRequested(dynamic gameId)
        {
            return This().GameRequests().Any(new { GameId = gameId });  //awesome
        }

        public bool OwnsGame(dynamic gameId)
        {
            return This().Games().Any(new { Id = gameId });  //awesome
        }

        public bool PrefersGame(dynamic gameId)
        {
            return !This().NotInterestedGames().Any(new { GameId = gameId });  //awesome
        }

        private bool SharesConsole(dynamic console)
        {
            return This().Games().Any(new { Console = console }) || !HasGames();  //awesome
        }

        public void AddFriend(dynamic user)
        {
            friendAssociations.Insert(new { UserId = This().Id, IsFollowing = user.Id });
        }

        public void RemoveFriend(dynamic friend)
        {
            if (friend == null) return;

            var friendAssociation = This().FriendAssociations().First(new { IsFollowing = friend.Id });

            if (friendAssociation == null) return;

            friendAssociations.Delete(friendAssociation.Id);
        }

        public void RequestGame(dynamic gameId, dynamic fromUserId)
        {
            gameRequests.Insert(new { UserId = This().Id, gameId, fromUserId });
        }

        public void MarkGameNotInterested(dynamic gameId)
        {
            notInterestedGames.Insert(new { UserId = This().Id, gameId });
        }
    }

###Schema generation in C# called Seed

If only all schema generation was this easy....

    [LocalOnly]
    public class SeedController : Controller
    {
        public Seed Seed { get; set; }

        public SeedController()
        {
            Seed = new Seed();
        }

        [HttpPost]
        public ActionResult All()
        {
            CreateUsers();
            CreateGames();
            CreateLibrary();
            CreateFriends();
            CreateNotInterestedGames();
            CreateGameRequests();
            AddConsoleToGames();
            return new EmptyResult();
        }

        private void CreateUsers()
        {
            Seed.CreateTable("Users", new dynamic[] 
            { 
                Id(),
                new { Email = "nvarchar(1000)" },
                new { Password = "<PASSWORD>)" },
                new { Handle = "nvarchar(1000)" }
            }).ExecuteNonQuery();
        }

        private void CreateGames()
        {
            Seed.CreateTable("Games", new dynamic[] {
                Id(),
                new { Name = "nvarchar(1000)" }
            }).ExecuteNonQuery();
        }

        private void CreateLibrary()
        {
            Seed.CreateTable("Library", new dynamic[] {
                Id(),
                UserId(),
                GameId(),
            }).ExecuteNonQuery();
        }

        private void CreateFriends()
        {
            Seed.CreateTable("FriendAssociations", new dynamic[] {
                Id(),
                UserId(),
                new { IsFollowing = "int", ForeignKey = "Users(Id)" }
            }).ExecuteNonQuery();
        }

        private void CreateNotInterestedGames()
        {
            Seed.CreateTable("NotInterestedGames", new dynamic[] { 
                Id(),
                UserId(),
                GameId(),
            }).ExecuteNonQuery();
        }

        private void CreateGameRequests()
        {
            Seed.CreateTable("GameRequests", new dynamic[] {
                Id(),
                UserId(),
                GameId(),
                new { FromUserId = "int", ForeignKey = "Users(Id)" },
            }).ExecuteNonQuery();
        }

        private void AddConsoleToGames()
        {
            Seed.AddColumns("Games", new dynamic[] 
            { 
                new { Console = "nvarchar(255)" }
            }).ExecuteNonQuery();
        }

        private object Id()
        {
            return new { Id = "int", Identity = true, PrimaryKey = true };
        }

        private object UserId()
        {
            return new { UserId = "int", ForeignKey = "Users(Id)" };
        }

        private object GameId()
        {
            return new { GameId = "int", ForeignKey = "Games(Id)" };
        }

        [HttpPost]
        public ActionResult PurgeDb()
        {
            Seed.PurgeDb();

            return new EmptyResult();
        }
    }

###General dev assistance in creating schema, building, deploying and running tests provided by [rake-dot-net](http://github.com/amirrajan/rake-dot-net)
**install-package rake-dot-net**

Here is a conversation between a new hire and his teammates:

New Ninja:<br/>
Hey, I'm new to the dev team, how do I get started?<br/>

Other Ninjas:<br/>
It's easy, you've got ruby, .Net, SqlExpress and IISExpress on your computer right?<br/>

New Ninja:<br/>
Of course! Who wouldn't???<br/>

Other Ninjas:<br/>
Wonderful. Get lastest.<br/>

New Ninja:<br/>
Okay. Done.<br/>

Other Ninjas:<br/>
Alright now create your dev and tests database using Management Studio.<br/>

New Ninja:<br/>
Okay. Done.<br/>

Other Ninjas:<br/>
Run `rake` to build the app<br/>
Run `rake server` to start up IIS Express<br/>
Run `rake tests` to run tests<br/>
Run `rake sample` to create sample data and generate schema<br/>
Run `rake -T` if you forget any of this<br/>
Now just go to http://localhost:3000/ to see the website<br/>

New Ninja:<br/>
OMG that was easy.<br/>

###A dynamic ModelBinder called ParamsModelBinder

Hey look, no need to create ViewModels or Data Transfer Objects any more:

    //global asax
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        ModelBinders.Binders.Add(typeof(object), new ParamsModelBinder()); //boo-ya
    }

    //Blog controller action for updating a blog entry
    [HttpPost]
    [ActionName("Edit")]
    [ValidateInput(false)]
    public dynamic Update(dynamic @params)
    {
        var blog = Blogs.Single(@params.id); //awesome

        blog.Title = @params.title; //awesome
        blog.Body = @params.body; //awesome

        if(!blog.IsValid())
        {
            ViewBag.Flash = blog.FirstError();
            ViewBag.Blog = blog;
            return View();
        }

        Blogs.Save(blog);

        return RedirectToAction("get", new { id = @params.id });
    }


<commit_msg>more readme updates<commit_after>##Why Oak?

Oak is about getting things done. It's about maintaining a rapid development cycle. It's about having an immediate and addictive feedback loop in place to keep you in the zone. It's about getting developers on board and getting them up and running fast so that they can start contributing. It's about reducing all aspects of development friction.

##How Oak?
Oak does this by leveraging dynamic constructs in C# 4.0. These constructs make testing frictionless.  Oak maintains a rapid development cycle by making testing easy and by providing an immediate feedack loop using Growl For Windows.  Oak takes advantage of dynamic C# 4.0 and metaprogramming for your data access.  Initial schema creation, seeding sample data, and regenerating database for other developers is provided directly through controller actions that can be leveraged by tests and rake scripts.  Oak is built on top of ASP.NET MVC, so you don't have to re-learn anything, just build upon what you already know.

##What Oak?
- MIT License
- Head over to the [wiki](https://github.com/amirrajan/oak/wiki) for additional details.
- [Reference Implementations](https://github.com/amirrajan/Oak/tree/master/Sample%20Apps) are included, test driven, awesome 
- Built ground up [under test](https://github.com/amirrajan/Oak/tree/master/Oak.Tests)
- Available via Nuget: **install-package oak or install-package oak-edge**

###Continous testing provided by [NSpec](http://nspec.org) and [SpecWatchr (screen cast)](http://nspec.org/continuoustesting)
**install-package nspec and install-package specwatchr**

Here is a NSpec test for one of the sample apps:

    //tests shows user registration
    class describe_AccountController : _borrowed_games
    {
        AccountController controller;
        dynamic user;
        bool authenticated;

        void before_each()
        {
            controller = new AccountController();
            MockSession(controller);
            controller.Authenticate = s => authenticated = true;
        }

        void registering_for_site()
        {
            context["requesting registration page"] = () =>
            {
                act = () => result = controller.Register();
                it["returns view"] = () => (result as object).should_cast_to<ViewResult>();
            };

            context["user registers"] = () =>
            {
                act = () => result = controller.Register(new
                {
                    Email = "<EMAIL>",
                    Password = "password",
                    PasswordConfirmation = "password"
                });

                it["logs in user"] = () => (result.Url as string).should_be("/");
                it["authenticates user"] = () => authenticated.should_be_true();
                it["sets user in session"] = () => ((decimal)controller.CurrentUser).should_be((decimal)user);

                context["user name is taken"] = () =>
                {
                    before = () => GivenUser("<EMAIL>");
                    it["return error stating that user name is taken"] = () =>
                        (result.ViewBag.Flash as string).should_be("Email is unavailable.");
                };
            };

            context["registration is invalid"] = () =>
            {
                act = () => result = controller.Register(new { Email = default(string), Password = default(string) });
                it["returns error stating that email is required."] = () => 
                    (result.ViewBag.Flash as string).should_be("Email is required.");
            };
        }
    }

###A Rails inspired implementation of ActiveModel called DynamicModel

Here is a "complex" model declaration in Oak:

    public class User : DynamicModel
    {
        Users users = new Users();
        FriendAssociations friendAssociations = new FriendAssociations();
        Games games = new Games();
        Library library = new Library();
        NotInterestedGames notInterestedGames = new NotInterestedGames();
        GameRequests gameRequests = new GameRequests();

        public User(dynamic dto)
        {
            Init(dto);
        }

        //Relationships of User
        public IEnumerable<dynamic> Associates()
        {
            yield return
            new HasManyThrough(users, through: friendAssociations, named: "Friends") { ForeignKey = "IsFollowing" };

            yield return
            new HasManyThrough(games, through: library);

            yield return
            new HasMany(library);

            yield return
            new HasMany(notInterestedGames);

            yield return
            new HasMany(gameRequests);

            yield return
            new HasMany(friendAssociations);
        }

        //User Validation
        public IEnumerable<dynamic> Validates()
        {
            yield return
            new Format("Handle") { With = "^@", ErrorMessage = "Your handle has to start with an '@' sign." };

            yield return
            new Format("Handle") { With = "^[\\S]*$", ErrorMessage = "Your handle can't contain any spaces." };

            yield return
            new Exclusion("Handle") { In = new[] { "@nameless" }, ErrorMessage = "You did not specify a handle." };

            yield return
            new Format("Handle") { With = "[a-zA-Z0-9]+", ErrorMessage = "You did not specify a handle." };

            yield return
            new Format("Handle") { With = "^@[a-zA-Z0-9]*$", ErrorMessage = "Your handle can only be alpha numeric." };

            yield return
            new Uniqueness("Handle", users) { ErrorMessage = "The handle is already taken." };  //awesome
        }

        //Complex Query Made Easy
        public IEnumerable<dynamic> PreferredGames()
        {
            //inner join is handled for you
            var gamesForFriends = This().Friends().Games() as IEnumerable<dynamic>;  //awesome

            var distinctPreferredGames =
                gamesForFriends
                    .Where(s => !OwnsGame(s.Id) && PrefersGame(s.Id) && SharesConsole(s.Console))
                    .Select(game => new
                    {
                        game.Id,
                        game.Name,
                        game.Console,
                        Requested = HasGameBeenRequested(game.Id),
                        Owner = game.User().Select("Id", "Handle")
                    })
                    .OrderBy(s => s.Requested ? 0 : 1)
                    .ThenBy(s => s.Name);

            return distinctPreferredGames;
        }
       
        //who needs extension methods?
        public bool HasGame(dynamic game)
        {
            return This().Games().Any(new { Id = game.Id });  //awesome
        }

        public bool HasGames()
        {
            return This().Games().Any();  //awesome
        }

        public bool HasFriends()
        {
            return This().Friends().Any();  //awesome
        }

        public bool HasGameBeenRequested(dynamic gameId)
        {
            return This().GameRequests().Any(new { GameId = gameId });  //awesome
        }

        public bool OwnsGame(dynamic gameId)
        {
            return This().Games().Any(new { Id = gameId });  //awesome
        }

        public bool PrefersGame(dynamic gameId)
        {
            return !This().NotInterestedGames().Any(new { GameId = gameId });  //awesome
        }

        private bool SharesConsole(dynamic console)
        {
            return This().Games().Any(new { Console = console }) || !HasGames();  //awesome
        }

        public void AddFriend(dynamic user)
        {
            friendAssociations.Insert(new { UserId = This().Id, IsFollowing = user.Id });
        }

        public void RemoveFriend(dynamic friend)
        {
            if (friend == null) return;

            var friendAssociation = This().FriendAssociations().First(new { IsFollowing = friend.Id });

            if (friendAssociation == null) return;

            friendAssociations.Delete(friendAssociation.Id);
        }

        public void RequestGame(dynamic gameId, dynamic fromUserId)
        {
            gameRequests.Insert(new { UserId = This().Id, gameId, fromUserId });
        }

        public void MarkGameNotInterested(dynamic gameId)
        {
            notInterestedGames.Insert(new { UserId = This().Id, gameId });
        }
    }

###Schema generation in C# called Seed

If only all schema generation was this easy....

    [LocalOnly]
    public class SeedController : Controller
    {
        public Seed Seed { get; set; }

        public SeedController()
        {
            Seed = new Seed();
        }

        [HttpPost]
        public ActionResult All()
        {
            CreateUsers();
            CreateGames();
            CreateLibrary();
            CreateFriends();
            CreateNotInterestedGames();
            CreateGameRequests();
            AddConsoleToGames();
            return new EmptyResult();
        }

        private void CreateUsers()
        {
            Seed.CreateTable("Users", new dynamic[] 
            { 
                Id(),
                new { Email = "nvarchar(1000)" },
                new { Password = "<PASSWORD>)" },
                new { Handle = "nvarchar(1000)" }
            }).ExecuteNonQuery();
        }

        private void CreateGames()
        {
            Seed.CreateTable("Games", new dynamic[] {
                Id(),
                new { Name = "nvarchar(1000)" }
            }).ExecuteNonQuery();
        }

        private void CreateLibrary()
        {
            Seed.CreateTable("Library", new dynamic[] {
                Id(),
                UserId(),
                GameId(),
            }).ExecuteNonQuery();
        }

        private void CreateFriends()
        {
            Seed.CreateTable("FriendAssociations", new dynamic[] {
                Id(),
                UserId(),
                new { IsFollowing = "int", ForeignKey = "Users(Id)" }
            }).ExecuteNonQuery();
        }

        private void CreateNotInterestedGames()
        {
            Seed.CreateTable("NotInterestedGames", new dynamic[] { 
                Id(),
                UserId(),
                GameId(),
            }).ExecuteNonQuery();
        }

        private void CreateGameRequests()
        {
            Seed.CreateTable("GameRequests", new dynamic[] {
                Id(),
                UserId(),
                GameId(),
                new { FromUserId = "int", ForeignKey = "Users(Id)" },
            }).ExecuteNonQuery();
        }

        private void AddConsoleToGames()
        {
            Seed.AddColumns("Games", new dynamic[] 
            { 
                new { Console = "nvarchar(255)" }
            }).ExecuteNonQuery();
        }

        private object Id()
        {
            return new { Id = "int", Identity = true, PrimaryKey = true };
        }

        private object UserId()
        {
            return new { UserId = "int", ForeignKey = "Users(Id)" };
        }

        private object GameId()
        {
            return new { GameId = "int", ForeignKey = "Games(Id)" };
        }

        [HttpPost]
        public ActionResult PurgeDb()
        {
            Seed.PurgeDb();

            return new EmptyResult();
        }
    }

###General dev assistance in creating schema, building, deploying and running tests provided by [rake-dot-net](http://github.com/amirrajan/rake-dot-net)
**install-package rake-dot-net**

Here is a conversation between a new hire and his teammates:

New Ninja:<br/>
Hey, I'm new to the dev team, how do I get started?<br/>

Other Ninjas:<br/>
It's easy, you've got ruby, .Net, SqlExpress and IISExpress on your computer right?<br/>

New Ninja:<br/>
Of course! Who wouldn't???<br/>

Other Ninjas:<br/>
Wonderful. Get lastest.<br/>

New Ninja:<br/>
Okay. Done.<br/>

Other Ninjas:<br/>
Alright now create your dev and tests database using Management Studio.<br/>

New Ninja:<br/>
Okay. Done.<br/>

Other Ninjas:<br/>
Run `rake` to build the app<br/>
Run `rake server` to start up IIS Express<br/>
Run `rake tests` to run tests<br/>
Run `rake sample` to create sample data and generate schema<br/>
Run `rake -T` if you forget any of this<br/>
Now just go to http://localhost:3000/ to see the website<br/>

New Ninja:<br/>
OMG that was easy.<br/>

###A dynamic ModelBinder called ParamsModelBinder

Hey look, no need to create ViewModels or Data Transfer Objects any more:

    //global asax
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        ModelBinders.Binders.Add(typeof(object), new ParamsModelBinder()); //boo-ya
    }

    //Blog controller action for updating a blog entry
    [HttpPost]
    [ActionName("Edit")]
    [ValidateInput(false)]
    public dynamic Update(dynamic @params)
    {
        var blog = Blogs.Single(@params.id); //awesome

        blog.Title = @params.title; //awesome
        blog.Body = @params.body; //awesome

        if(!blog.IsValid())
        {
            ViewBag.Flash = blog.FirstError();
            ViewBag.Blog = blog;
            return View();
        }

        Blogs.Save(blog);

        return RedirectToAction("get", new { id = @params.id });
    }



{'fraction_non_alphanumeric': 0.11075405425603467, 'fraction_numerical': 0.0023167177341005905, 'mean_word_length': 1.85167820990943, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 103, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6659633', 'n_tokens_mistral': 7152, 'n_tokens_neox': 6760, 'n_words': 2042}