npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

smart-objects

v0.3.0

Published

A library for creating event-y and validate-y objects in JS

Downloads

2

Readme

smart-objects

Smarts Objects allow you to create a blueprint of properties for an object. These properties can then be validated and have events fired when these properties are modified. Heavy influenced by the Ampersand State project. Smart Objects use definable properties so you don't have to bother with .set(), simple assignment will just work.

In short, use Smart Objects if:

  • You know exactly the structure of your object
  • Having default values on your object is useful
  • You want type checking on object properties
  • You have validation on object properties
  • You want to fire events whenever a property is modified
  • You want to add additional properties/functions on an object that aren't serialized

Creating a Smart-Object

Let's create a simple user model

var UserModel = smartObj({
	props : {
		name : 'string',
		age : 'number',
		posts : {
			type : 'array',
			validate : function(val){
				return val.length > 0;
			}
		},
		customData : 'object',
		userType : {
			tier : 'string',
			isAwesome : {
				type : 'boolean',
				default : false
			}
		},
		created_on : {
			type : 'number',
			default : Date.now
		},
		additionalData : 'any'
	}
});

Each smart object should have a props object outlining the properties you care about, along with their type. On creation, those properties are defaulted to that type's default value. So in our example,loggedInUser.posts will actually be initialized as an empty array. You get and set these properties just as you would normally, no worrying about .set() or .get() functions.

var loggedInUser = UserModel.create({ name : 'Bromley'});

if(!loggedInUser.userType.isAwesome){
	loggedInUser.additionalData = 'Not cool bro';
}

Events

Smart Objects fire change events whenever one of it's tracking properties is modified. change is always fired whenever anything changes. To listen to a specific property only, use the syntax 'change:propName'.

loggedInUser.on('change', function(propName){
	console.log(propName + 'has changed!');
});
loggedInUser.on('change:age', function(newAge){
	console.log('why are you changing your age?');
});
loggedInUser.age = 21; //Both events will fire

Type Checking

Property types are used for type checking whenever a property is set.

loggedInUser.name = 23;
//Type Error: Property 'name' only supports string

If you aren't sure what type the property will be, but still want to listen to events on it, you can use the type of 'any'.

loggedInUser.additionalData = true;
loggedInUser.additionalData = {powerLevel : 9001};
//No errors here!

Default Values

The property type can also be as an object containing a default field. This field can be a function, which will be called at creation time.

var UserModel = smartObj({
	props : {
		name : {
			type : 'string',
			default : 'New User'
		},
		age : 'number',
		posts : 'array',
		customData : {
			type : 'object',
			default : {junk : true}
		},
		userType : {
			tier : {
				type : 'string',
				default : 'bronze'
			},
			isAwesome : {
				type : 'boolean',
				default : false
			},
		},
		additionalData : {
			type : 'any',
			default : {}
		}
	}
});

var emptyUser = UserModel.create();

//What you get
{
	name : 'New User',
	age : undefined,
	posts : [],
	customData : {junk : true},
	userType : {
		tier : 'bronze',
		isAwesome : false
	},
	additionalData : {}
}

Validation

Each smart object will have access to a .validate() function which will either return true or an object mapped with error messages. Each prop can have it's own validation function defined on the definition object as validate. The value of the prop will be passed in as a parameter, and the function context will be the smart object itself. To mark a field as invalid, either return false or a custom error string.

var UserModel = smartObj({
	props : {
		name : {
			type : 'string',
			validate : function(name){
				return !!name
			}
		},
		age : 'number',
		posts : 'array',
		customData : 'object',
		userType : {
			tier : 'string',
			isAwesome : {
				type : 'boolean',
				default : false,
				validate : function(val){
					if(!val) return 'Must be Awesome'
				}
		}
	}
});

var loggedInUser = UserModel.create({ name : 'Bromley'});

loggedInUser.validate();
//Returns
{
	name : 'Invalid',
	userType : {
		isAwesome : 'Must be Awesome'
	}
}

Methods

We can also define functions we want to be added to each smart object along with the props.

var UserModel = smartObj({
	props : {
		name : 'string',
		age : 'number',
		posts : 'array',
		customData : 'object',
		userType : {
			tier : 'string',
			isAwesome : 'boolean'
		}
	},
	methods : {
		hasManyPosts : function(){
			return this.posts.length > 10;
		},
		toggleAwesome : function(){
			this.userType.isAwesome = !this.userType.isAwesome;
		}
	}
});

var loggedInUser = UserModel.create({ name : 'Bromley'});

loggedInUser.hasManyPosts(); //false

Statics

Static functions are added to the smart-object definition, but not the instance. This is useful for when you have a function that deals with your smart-objects, not a specific instance, or we want a function that will create many instances given criteria, such as .fetchAllActiveUsers().

var UserModel = smartObj({
	props : {
		name : 'string',
		age : 'number',
		posts : 'array',
		customData : 'object',
		userType : {
			tier : 'string',
			isAwesome : ['boolean', false]
		}
	},
	methods : {
		hasManyPosts : function(){
			return this.posts.length > 10;
		},
		toggleAwesome : function(){
			this.userType.isAwesome = !this.userType.isAwesome;
		}
	},
	statics : {
		getRecentUsers : function(){
			//...
		}
	}
});

Built-in Goodies

Each Smart Object comes with a few built-in goodies

set   smart_obj.set([object]) Set allows you to massively update your object's properties. Useful for initially defining a smart object with defaults and triggers waiting on response from an API, then setting that response to your object. This will do a validation check on each property as well.

toJSON   smart_obj.toJSON() Returns a JSON version of your smart object without any of the getters, setters, or any additional properties you may have added. Perfect for shipping data off to an API.

_props   smart_obj._props The smart object's properties are actually stored at _props. If you want to be sneaky and change some values without firing change events or not have it validated, you can do it here.

loggedInUser._props.age = 21;
loggedInUser.age; //it's 21! and not a single event was fired.