<commit_before>Jermaine is a JavaScript data modeling tool. Drawing inspiration from 
Jasmine, Mongoose, and Rails, it's designed to help create robust and
readable JavaScript data models.

Thanks to the folks at NEMAC (nemac.org) for allowing me to release
this really early version under an MIT license.

It's still under heavy development, but feedback or contributions are
welcome.

Models are created like this:

    var Person = new Model();

Then you can add properties and methods to the model like this:

    Person.hasA("firstName");
    Person.hasA("lastName");
    Person.hasAn("id");

    Person.hasMany("friends").which.validateWith(function (friend) {
        return friend instanceof Person;
    });

    Person.attribute("id").validatesWith(function (id) { 
        return typeof(id) === "Number";
    });

    Person.respondsTo("isFriendsWith", function (friend) {
        this.friends().add(friend);
    });


If you're comfortable with anonymous functions, you can send a specification
function to the constructor:

    var Person = new Model(function () {
        //'this' is pointing to the model
        this.hasA("firstName");
        this.hasA("lastName");
        this.hasAn("id");
        this.hasMany("friends");

        this.attribute("friends").validatesWith(function (friend) {
            return friend instanceof Person;
        });

        this.respondsTo("isFriendsWith", function (friend) {
            //in a method specification, 'this' points to
            //the calling object, NOT the model
            this.friends().add(friend);
            friend.friends().add(this);
        });
    });



Finally, you can create a person by calling the model as a constructor:

    var p = new Person(),
        s = new Person(),
        j = new Perons();

And manipulate the objects:

    p.firstName("Mark").lastName("Phillips");
    s.firstName("Semmy").lastName("Purewal");
    j.firstName("John").lastName("Frimmel");

    p.isFriendsWith(s);
    s.isFriendsWith(j);
    j.isFriendsWith(p);

If you later change the model, all new objects will be created with the updated
model spec, but old objects will not enjoy the new features:

   Person.hasAn("id");

   var a = new Person();
   a.firstName("John").lastName("Maxwell").id(500);  //fine

   p.id(501); //throws an error


Some basic validation is built in as well, so you can define more robust models:

var Card,
    Deck,
    suits = ["clubs", "diamonds", "hearts", "spades"],
    ranks = ["2","3","4","5","6","7","8","9","10","J","Q","K","A"];
        
Card = new Model(function () {
     this.isImmutable();
     this.hasA("suit").which.isOneOf(suits);
     this.hasA("rank").which.isOneOf(ranks);

     this.isBuiltWith("rank","suit");
});

Deck = new Model(function () {
     this.hasMany("cards").which.validateWith(function (card) {
         return card instanceof Card;
     });

     this.isBuiltWith(function () {
          for (suit = 0; suit < suits.length; suit++) {
               for (rank = 0; rank < ranks.length; rank++) {
                   this.cards().add(new Card(ranks[rank], suits[suit]));
               }
           }
     });
});

Right now, you can do the following with a Model object:
//attribute related
hasA -- creates an encapsulated attribute
hasAn -- syntactic sugar for hasA
hasMany -- creates an encapsulated list of encapsulated attributes
attributes -- returns an array of attribute names as strings<commit_msg>fix some problems with README<commit_after>Jermaine is a JavaScript data modeling tool. Drawing inspiration from 
Jasmine, Mongoose, and Rails, it's designed to help create robust and
readable JavaScript data models.

Thanks to the folks at NEMAC (nemac.org) for allowing me to release
this really early version under an MIT license.

It's still under heavy development, but feedback or contributions are
welcome.

Models are created like this:

    var Person = new Model();

Then you can add properties and methods to the model like this:

    Person.hasA("firstName");
    Person.hasA("lastName");
    Person.hasAn("id");

    Person.hasMany("friends").which.validateWith(function (friend) {
        return friend instanceof Person;
    });

    Person.attribute("id").validatesWith(function (id) { 
        return typeof(id) === "Number";
    });

    Person.respondsTo("isFriendsWith", function (friend) {
        this.friends().add(friend);
    });


If you're comfortable with anonymous functions, you can send a specification
function to the constructor:

    var Person = new Model(function () {
        //'this' is pointing to the model
        this.hasA("firstName");
        this.hasA("lastName");
        this.hasAn("id");
        this.hasMany("friends");

        this.attribute("friends").validatesWith(function (friend) {
            return friend instanceof Person;
        });

        this.respondsTo("isFriendsWith", function (friend) {
            //in a method specification, 'this' points to
            //the calling object, NOT the model
            this.friends().add(friend);
            friend.friends().add(this);
        });
    });



Finally, you can create a person by calling the model as a constructor:

    var p = new Person(),
        s = new Person(),
        j = new Perons();

And manipulate the objects:

    p.firstName("Mark").lastName("Phillips");
    s.firstName("Semmy").lastName("Purewal");
    j.firstName("John").lastName("Frimmel");

    p.isFriendsWith(s);
    s.isFriendsWith(j);
    j.isFriendsWith(p);

If you later change the model, all new objects will be created with the updated
model spec, but old objects will not enjoy the new features:

    Person.hasAn("id");

    var a = new Person();
    a.firstName("John").lastName("Maxwell").id(500);  //fine

    p.id(501); //throws an error


Some basic validation is built in as well, so you can define more robust models:

    var Card,
        Deck,
        suits = ["clubs", "diamonds", "hearts", "spades"],
        ranks = ["2","3","4","5","6","7","8","9","10","J","Q","K","A"];
        
    Card = new Model(function () {
        this.isImmutable();
        this.hasA("suit").which.isOneOf(suits);
        this.hasA("rank").which.isOneOf(ranks);

        this.isBuiltWith("rank","suit");
    });

    Deck = new Model(function () {
        this.hasMany("cards").which.validateWith(function (card) {
            return card instanceof Card;
        });

        this.isBuiltWith(function () {
            for (suit = 0; suit < suits.length; suit++) {
                for (rank = 0; rank < ranks.length; rank++) {
                    this.cards().add(new Card(ranks[rank], suits[suit]));
                }
            }
        });
    });

Right now, you can do the following with a Model object:
//attribute related
hasA -- creates an encapsulated attribute
hasAn -- syntactic sugar for hasA
hasMany -- creates an encapsulated list of encapsulated attributes
attributes -- returns an array of attribute names as strings
{'fraction_non_alphanumeric': 0.13075268817204302, 'fraction_numerical': 0.005161290322580645, 'mean_word_length': 2.832967032967033, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, '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': '2665097', 'n_tokens_mistral': 2025, 'n_tokens_neox': 1924, 'n_words': 633}