// Create first Module
$Unit(function(unit, root, glob, None){
// Importing required classes
unit.ImportFrom("acme", ["stars.*", "beings.*", "stuff.*"])
// Create package
this.Package("acme.milkyway.solarsystem", function(solarsystem){
// Create our first class and bind it to the group namespace
solarsystem.Earth = unit.buildClass(
// class name
"Earth",
// list of ancestors
[unit.Rivers, unit.Birds, unit.Animals, unit.Human],
// Class role (specification), where are declared all class props and its configuration.
// Idea of role syntax come from ECMAScript 5'th property descriptors and mostly related
// to it as opposite values. For example "internal" descriptor = true is a shortcut
// for enumerable, writable and configurable descriptors = false, hidden = true is
// enumerable = false and so on ... By default "public" descriptor is true.
// It is also required that type of a property is defined.
// Role record can be string, number or object.
{
"name" : "String",
"earth_init": {
type: "Function",
constructor: true,
argumentType: ["String", "Number", "Number", "Number"],
defaultArguments: [1800, 70, 20],
restArgument: true,
docs: "Earth constructor"
}
},
// class body, within it this context is class prototype
function(__class__){
// initialize properties with default value (it is required)
this.name = None
// constructor
this.earth_init = function(self, name, rivers, birds, animals, args) {
// initialize ancestors
self.rivers_init(rivers)
self.birds_init(birds)
self.animals_init(animals)
self.name = name
trace(__class__.className, "was created. Rest arguments", args)
}
},
// Last but not least set a parent of the Earth
unit.Sun
);
},{
// Export list
Earth : "Class"
})
// Finally, test our example
unit.Import(["acme.milkyway.solarsystem.Earth"])
// Create a new instance of the Sun
var star = unit.Sun()
// Create a new instance of the Earth
var earth = unit.Earth("Terra", 5300, 940, 520, 80, 15)
var man = earth.createHuman()
trace("His name is " + man.name)
setTimeout(function(){
// do nothing
}, 60*60*24*7)
})
from acme import stars.*, beings.*, stuff.*
package acme.milkyway.solarsystem
{
export Earth
class Earth(Rivers, Birds, Animals, Human) childof Sun {
role Earth {
public prop name typeof String: "",
public constructor earth_init( name typeof String,
rivers typeof Number,
birds typeof Number,
animals typeof Number ):
"Earth constructor"
}
name = None
function earth_init(self, name, rivers=1800, birds=70, animals=20, *args){
self.rivers_init(rivers)
self.birds_init(birds)
self.animals_init(animals)
self.name = name
trace("Earth was created. Rest arguments", args)
}
}
}
// Finally, test our example
import acme.milkyway.solarsystem.Earth
// Create a new instance of the Sun
let star = Sun()
// Create a new instance of the Earth
let earth = Earth("Terra", 5300, 940, 520, 80, 15)
let man = earth.createHuman()
trace("His name is " + man.name)
setTimeout(function(){
// do nothing
}, 60*60*24*7)
$Unit(function(unit, _, _, None)
{
// debug helper
function raises(f){
try{
f()
} catch(e) {
trace(e)
}
}
// i will create new class with virtual props "name" and "age",
// note that getter and setter have to be name of internal function
unit.buildClass("Person", [],
{
person_init: {
type: "function",
constructor: true
},
name: {
type: "string",
get: "get_name",
set: "set_name"
},
age: {
// i used uint type for age to catch arithmetic errors,
// e.g. function returned unexpected NaN (which has Number type by the way)
type: "uint",
get: "get_age",
set: "set_age"
}
},
function()
{
// private
var roster = []
// public
this.person_init = function(self, name)
{
self.id = roster.length
roster.push({name: None, yearOfBirth: None})
self.name = name
}
this.get_name = function(self)
{
return roster[self.id].name
}
this.set_name = function(self, name)
{
roster[self.id].name = name
}
this.get_age = function(self)
{
var age = (new Date).getFullYear() - roster[self.id].yearOfBirth
// test for age is not NaN
return age && Uint(age)
}
this.set_age = function(self, age)
{
roster[self.id].yearOfBirth = (new Date).getFullYear() - age
}
})
var bob
// Person constructor required at least one passed argument,
// so that next call will raise an error
raises(function(){
bob = unit.Person()
})
bob = unit.Person("Bob")
// if we are trying to get Bob's age before setting his
// date of birth, we got only type exception due to calculation error
raises(function(){
bob.age
})
// in the same time assigning value with inappropriate type will fail
raises(function(){
bob.age = 56
})
// age property required integer number to assign
bob.age = Uint(56)
// check results
trace(bob.name, bob.age)
})
// class Anonymous
function Anonymous(context, args){
// to check whether function is called from class constructor
// (it is required to keep parenting chain intact)
if( context === Anonymous ) {
this.abc = args[0]
}
}
// substitute function constructor with class constructor
Anonymous.new = Class.new
// register class to change object type
Class.registerClasses([Anonymous])
// finally static method 'new()' is a class constructor, and Anonymous is a new unique type
var anon = Anonymous.new("abc")
trace( Type.is_a(Anonymous, Class) ) // => true
trace( Type.is_a(anon, Anonymous) ) // => true
$Unit(function(unit) {
// She is Alice
unit.buildClass("Alice", [unit.Signal],
{
"alice_init": {
type: "Function",
constructor: true
}
},
function() {
this.alice_init = function(self){
self.signal_init()
self.addSignalListener("sayhello", function(msg){
if( Type.is_a( msg.from, unit.Bob ) ) {
trace("Alice: Hello!")
msg.from.raise("hello")
}
})
}
})
// He is Bob
unit.buildClass("Bob", [unit.Signal],
{
"bob_init": {
type: "Function",
constructor: true
}
},
function() {
this.bob_init = function(self, she){
self.signal_init()
self.addSignalListener("hello", function(msg){
trace("Bob: That's all she said.")
})
she.raise({type:"sayhello", from:self})
}
})
// They have a short conversation
unit.Bob( unit.Alice() )
})
$Unit(function(unit)
{
// Create dict from pairs [key, value]
var lib = unit.Dictionary([
["0345453743", {
title: "The Ultimate Hitchhiker's Guide to the Galaxy",
author: "douglasadams"}],
["0545139708", {
title: "Harry Potter and the Deathly Hallows",
author: "jkrowling"}],
["014028334X", {
title: "One Flew Over the Cuckoo's Nest",
author: "kenkesey"}],
])
// Create from object
var authors = unit.Dictionary({
"douglasadams": {
"fullname": "Douglas Adams"
},
"jkrowling": {
"fullname": "J.K. Rowling"
}
})
function searchBook(queryISBN)
{
var result
for( var i=0; i<lib.length; i++)
{
var bookISBN = lib.keyAtIndex(i)
var book = lib.valueAtIndex(i)
// Important difference from array is that dict returns exception if such
// entry were not found
var author
try {
author = authors.__get__(book.author).fullname
} catch(e) {
if( unit.Type.is_a(e, unit.KeyError) ){
author = "Unknown"
} else {
throw e
}
}
if( bookISBN == queryISBN )
{
result = {
author: author,
title: book.title
}
}
}
return result
}
var book = searchBook("014028334X")
trace("Author:", book.author)
trace("Title:", book.title)
})
$Unit(function(unit)
{
unit.Import(["dummyxml.*"])
XML.prettyPrinting = false
var contacts = unit.Dictionary({
"Milagros Pokorny": "poko1004@wp.pl",
"Guy Rippe": "rippy@foolkick.com",
"Erik Leja": "leja@lazyelephant.org"
})
unit.contacts_count = contacts.length
var xml = unit.DummyXML("<contacts total={contacts_count}/>")
contacts.forEachPair(function(pair){
var name = pair[0]
var email = pair[1]
var person = xml.append("<person/>")
person.append("<name/>").append(name)
person.append("<email/>").append(email)
})
xml = unit.DummyXML(xml.toXMLString())
trace("Total contacts:", xml.attr("total"))
xml.forEachElm(function(person){
trace("Name:", person.elm("name").text())
trace("Email:", person.elm("email").text())
})
})
// Stuff
$Unit(function(unit){
unit.export2("acme.beings",
{
Birds:
unit.buildClass("Birds", [],
{
"birds_init" : {
type: "Function",
constructor: true
}
},
function(){
this.birds_init = function(self, number){
trace(number + " of birds were created")
}
}),
Animals:
unit.buildClass("Animals", [],
{
"animals_init" : {
type: "Function",
constructor: true
}
},
function(){
this.animals_init = function(self, number){
trace(number + " of animals were created")
}
}),
Human:
unit.buildClass("Human", [],
{
"human_init" : {
type: "Function",
constructor: true
},
"createHuman" : {
type: "Function"
}
},
function(){
this.human_init = function(self){
self.name = "Adam"
trace("A human created")
}
this.createHuman = function(self){
return unit.Human()
}
})})
unit.export2("acme.stars",
{
Sun:
unit.buildClass("Sun", [],
{
"sun_init" : {
type: "Function",
constructor: true
}
},
function(){
this.sun_init = function(self){
trace("Sun was created")
}
})})
unit.export2("acme.stuff",
{
Rivers:
unit.buildClass("Rivers", [],
{
"rivers_init" : {
type: "Function",
constructor: true
}
},
function(){
this.rivers_init = function(self, number){
trace(number + " of rivers were created")
}
})})
})
/**
* Enumerable bug
* --------------
*
* Enumerable bug cause iterator to return property name
* if it was defined in prototype despite the fact that property of class instance was
* marked as not enumerable.
* Almost all JS engines except SpiderMonkey (Firefox and Co) affected this bug.
* Damn! This stop me from using defineProperty extension at all!...
*
* Results
* -------
*
* IE9: a,b
* Chrome: a,b
* Safari: a,b
* Node.js: a,b
* Opera: failed
* Firefox: nothing - right job
*
* So while this bug will not be fixed, platform will be marked as
* not ECMAScript-5 compliant.
*/
function test() {Object.defineProperty(this, "a", {value : "b",
writable : true,
enumerable : false,
configurable : true});
}
test.prototype.a = "c"
t = new test()
result = null
for(x in t) result = [x, t[x]]
trace(result == "a,b" ? "failed" : "passed")
Code snippets: yours, earth.js, earth.joo, person.js, casting.js, conversation.js, books.js, contacts.js, stuff.js, enumerable_bug.js