diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..caae816 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,85 @@ +# Contributing to can-define + +Check out the [contribution guide on CanJS.com](https://canjs.com/doc/guides/contribute.html) for information on: + +- [Code of Conduct](https://canjs.com/doc/guides/contribute.html#CodeofConduct) +- [Getting Help](https://canjs.com/doc/guides/contribute.html#GettingHelp) +- [Project Organization](https://canjs.com/doc/guides/contributing/project-organization.html) +- [Reporting Bugs](https://canjs.com/doc/guides/contributing/bug-report.html) +- [Suggesting Features](https://canjs.com/doc/guides/contributing/feature-suggestion.html) +- [Finding Ways to Contribute](https://canjs.com/doc/guides/contributing/finding-ways-to-contribute.html) + +The rest of this guide has information that’s specific to this repository. + +## Developing Locally + +This section will walk you through setting up the [repository](https://github.com/canjs/can-define) on your computer. + +### Signing up for GitHub + +If you don’t already have a GitHub account, you’ll need to [create a new one](https://help.github.com/articles/signing-up-for-a-new-github-account/). + +### Forking & cloning the repository + +A “fork” is a copy of a repository in your personal GitHub account. “Cloning” is the process of getting the repository’s source code on your computer. + +GitHub has a guide for [forking a repo](https://help.github.com/articles/fork-a-repo/). To fork can-define, you can start by going to its [fork page](https://github.com/canjs/can-define/fork). + +Next, you’ll want to clone the repo. [GitHub’s cloning guide](https://help.github.com/articles/cloning-a-repository/) explains how to do this on Linux, Mac, or Windows. + +GitHub’s guide will [instruct you](https://help.github.com/articles/fork-a-repo/#step-2-create-a-local-clone-of-your-fork) to clone it with a command like: + +```shell +git clone https://github.com/YOUR-USERNAME/can-define +``` + +Make sure you replace `YOUR-USERNAME` with your GitHub username. + +### Installing the dependencies + +After you’ve forked & cloned the repository, you’ll need to install the project’s dependencies. + +First, make sure you’ve [installed Node.js and npm](https://docs.npmjs.com/getting-started/installing-node). + +If you just cloned the repo from the command line, you’ll want to switch to the folder with your clone: + +```shell +cd can-define +``` + +Next, install the project’s dependencies with npm: + +```shell +npm install +``` + +### Starting the development server + +Run the following to start a dev server: + +```shell +npm run develop +``` + +### Running the tests + +You can manually run this repository’s tests in any browser by starting the dev server (see the section above) and visiting this page: http://localhost:8080/test/test.html + +Firefox is used to run the repository’s automated tests from the command line. If you don’t already have it, [download Firefox](https://www.mozilla.org/en-US/firefox/new/). Mozilla has guides for installing it on [Linux](https://support.mozilla.org/t5/Install-and-Update/Install-Firefox-on-Linux/ta-p/2516), [Mac](https://support.mozilla.org/t5/Install-and-Update/How-to-download-and-install-Firefox-on-Mac/ta-p/3453), and [Windows](https://support.mozilla.org/t5/Install-and-Update/How-to-download-and-install-Firefox-on-Windows/ta-p/2210). + +After Firefox is installed, you can run: + +```shell +npm test +``` + +### Making a build + +Run the following command to create a build: + +```shell +npm run build +``` + +This will create a `dist/` folder that contains the AMD, CommonJS, and global module versions of the project. + diff --git a/README.md b/README.md index f9b711e..0afd214 100644 --- a/README.md +++ b/README.md @@ -1,714 +1,26 @@ -[![Build Status](https://travis-ci.org/canjs/can-define.svg?branch=master)](https://travis-ci.org/canjs/can-define) - # can-define +[![Join the chat at https://gitter.im/canjs/canjs](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/canjs/canjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/canjs/can-define/blob/master/LICENSE) +[![npm version](https://badge.fury.io/js/can-define.svg)](https://www.npmjs.com/package/can-define) +[![Travis build status](https://travis-ci.org/canjs/can-define.svg?branch=master)](https://travis-ci.org/canjs/can-define) [![Greenkeeper badge](https://badges.greenkeeper.io/canjs/can-define.svg)](https://greenkeeper.io/) Add observable properties, type conversion, and getter/setter logic to your constructor prototypes. -- [__can-define__ function](#can-define-function) - - [can.define(prototype, propDefinitions)](#candefineprototype-propdefinitions) - - [propDefinition Object](#propdefinition-object) - - [types Object](#types-object) -- [__can-define/map/map__ function](#can-definemapmap-function) - - [new can.DefineMap([props])](#new-candefinemapprops) - - _static_ - - [can.DefineMap.extend([name,] [static,] prototype)](#candefinemapextendname-static-prototype) - - [seal Boolean](#seal-boolean) - - _prototype_ - - [map.each( callback(item, propName ) )](#mapeach-callbackitem-propname--) - - [map.toObject()](#maptoobject) - - [map.serialize()](#mapserialize) - - [map.get(propName)](#mapgetpropname) - - [map.set(propName, value)](#mapsetpropname-value) -- [__can-define/list/list__ function](#can-definelistlist-function) - - [new can.DefineList([items])](#new-candefinelistitems) - - _prototype_ - - [map.item(index, [newVal])](#mapitemindex-newval) - - [map.items()](#mapitems) - - [list.each( callback(item, index) )](#listeach-callbackitem-index-) - - [list.forEach(callback[, thisArg])](#listforeachcallback-thisarg) - - [list.splice(index[, howMany[, ...newElements]])](#listspliceindex-howmany-newelements) - - [list.replace(collection)](#listreplacecollection) - - [list.push(...elements)](#listpushelements) - - [list.unshift(...elements)](#listunshiftelements) - - [list.pop()](#listpop) - - [list.shift()](#listshift) - - [list.indexOf(item)](#listindexofitem) - - [list.join(separator)](#listjoinseparator) - - [list.reverse()](#listreverse) - - [list.slice([start[, end]])](#listslicestart-end) - - [list.concat(...args)](#listconcatargs) - -## API - - -## __can-define__ function -Exports the `can.define` method that defines observable properties and their behavior. - - - -### can.define(prototype, propDefinitions) - - -Define observable properties, type conversion, and getter/setter logic to your constructor prototypes. - -```js -var Person = function(first, last){ - this.first = first; - this.last = last; -}; -can.define(Person.prototype,{ - first: { - type: "string" - }, - last: { - type: "string" - }, - fullName: { - get: function(){ - return this.first+" "+this.last; - } - } -}); -``` - - -1. __prototype__ {Object}: - The prototype object of a constructor function. - -1. __propDefinitions__ {Object\}: - An object of - properties and their definitions. - -#### propDefinition `{Object}` - - -Defines the type, initial value, and get, set, and serialize behavior for an -observable property. - - - -##### Object - -- __value__ {function|*}: - Specifies the initial value of the property or - a function that returns the initial value. - - ```js - // A default age of `0`: - var Person = DefineMap.extend({ - age: {value: 0} - }); - - new Person().age //-> 0 - ``` - - `Object` types should not be specified directly on `value` because that same object will - be shared on every instance of the Map. Instead, a function that returns a fresh copy should be provided: - - ```js - // A default address object: - var Person = DefineMap.extend({ - address: { - value: function(){ - return {city: "Chicago", state: "IL"}; - }; - } - }); - - new Person().address //-> {city: "Chicago", state: "IL"}; - ``` - -- __Value__ {function}: - Specifies a function that will be called with `new` whose result is - set as the initial value of the attribute. - - ```js - // A default empty DefineList of hobbies: - var Person = DefineMap.extend({ - hobbies: {Value: DefineList} - }); - - new Person().hobbies //-> [] - ``` - -- __type__ {function|String}: - Specifies the type of the - property. The type can be specified as either a function - that returns the type coerced value or one of the [types](#types-object) names. - - ```js - var Person = DefineMap.extend({ - age: {type: "number"}, - hobbies: { - type: function(newValue){ - if(typeof newValue === "string") { - return newValue.split(",") - } else if( Array.isArray(newValue) ) { - return newValue; - } - } - } - }); - - var me = new Person({age: "33", hobbies: "bball,js"}) - me.age //-> 33 - me.hobbies //-> ["bball","js"] - ``` - -- __Type__ {function}: - A constructor function that takes - the value passed to [can.Map::attr attr] as the first argument and called with - new. For example, if you want whatever - gets passed to go through `new Array(newValue)` you can do that like: - - ```js - define: { - items: { - Type: Array - } - } - ``` - - If the value passed to [can.Map::attr attr] is already an Array, it will be left as is. - -- __set__ {can.Map.prototype.define.set}: - A set function that specifies what should happen when an attribute - is set on a [can.Map]. `set` is called with the result of `type` or `Type`. The following - defines a `page` setter that updates the map's offset: - - ```js - define: { - page: { - set: function(newVal){ - this.attr('offset', (parseInt(newVal) - 1) * - this.attr('limit')); - } - } - } - ``` - -- __get__ {[get](#get-lastsetvalue-)(lastSetValue)}: - A function that specifies how the value is retrieved. The get function is - converted to an [can.compute.async async compute]. It should derive its value from other values - on the map. The following - defines a `page` getter that reads from a map's offset and limit: - - ```js - define: { - page: { - get: function (newVal) { - return Math.floor(this.attr('offset') / - this.attr('limit')) + 1; - } - } - } - ``` - - A `get` definition makes the property __computed__ which means it will not be serialized by default. - -- __serialize__ {can.Map.prototype.define.serialize|Boolean}: - Specifies the behavior of the - property when [can.Map::serialize serialize] is called. - - By default, serialize does not include computed values. Properties with a `get` definition - are computed and therefore are not added to the result. Non-computed properties values are - serialized if possible and added to the result. - - ```js - Paginate = can.Map.extend({ - define: { - pageNum: { - get: function(){ return this.offset() / 20 } - } - } - }); - - p = new Paginate({offset: 40}); - p.serialize() //-> {offset: 40} - ``` - - If `true` is specified, computed properties will be serialized and added to the result. - - ```js - Paginate = can.Map.extend({ - define: { - pageNum: { - get: function(){ return this.offset() / 20 }, - serialize: true - } - } - }); - - p = new Paginate({offset: 40}); - p.serialize() //-> {offset: 40, pageNum: 2} - ``` - - If `false` is specified, non-computed properties will not be added to the result. - - ```js - Paginate = can.Map.extend({ - define: { - offset: { - serialize: false - } - } - }); - - p = new Paginate({offset: 40}); - p.serialize() //-> {} - ``` - - If a [can.Map.prototype.define.serialize serialize function] is specified, the result - of the function is added to the result. - - ```js - Paginate = can.Map.extend({ - define: { - offset: { - serialize: function(offset){ - return (offset / 20)+1 - } - } - } - }); - - p = new Paginate({offset: 40}); - p.serialize() //-> {offset: 3} - ``` - -#### types `{Object}` - - -Defines the type, initial value, and get, set, and serialize behavior for an -observable property. All type converters leave `null` and `undefined` as is except for -the `"boolean"` type converter. - - - -##### Object - -- __observable__ {function}: - The default type behavior. It converts plain Objects to - [DefineMaps](#new-candefinemapprops) and plain Arrays to [DefineLists](#new-candefinelistitems). Everything else is left as is. -- __any__ {function}: - Leaves the set value as is, performs no type conversion. Aliased as `*`. -- __string__ {function}: - Converts to a string with `""+val`. -- __date__ {function}: - Converts to a JavaScript date using `Date.parse(val)` if a string is given or `new Date(val)` if a number is passed. -- __number__ {function}: - Converts to a number with `+(val)`. -- __boolean__ {function}: - Converts to `false` if `val` is falsey, `"0"`, or `"false"`; otherwise, converts to `true`. -- __htmlbool__ {function}: - Like `boolean`, but converts to `true` if empty string (`""`) is passed. -- __compute__ {function}: - Allows computes to be passed and the property take on the value of the compute. -- __stringOrObservable__ {function}: - Converts plain Objects to [DefineMaps](#new-candefinemapprops), plain Arrays to [DefineLists](#new-candefinelistitems) and everything else to strings. This is useful for routing. - - -## __can-define/map/map__ function -Create observable objects. - - -### new can.DefineMap([props]) - - -Creates a new instance of DefineMap or an extended DefineMap. - -```js -var person = new can.DefineMap({ - first: "Justin", - last: "Meyer" -}) -``` - - -1. __props__ {Object}: - Properties and values to seed the map with. - -- __returns__ {[can-define/map/map](#new-candefinemapprops)}: - An instance of `can.DefineMap` with the properties from _props_. - - -#### can.DefineMap.extend([name,] [static,] prototype) - - -Extends can.DefineMap, or constructor functions derived from can.DefineMap, -to create a new constructor function. - -```js -var Person = can.DefineMap.extend( - "Person", - {seal: true}, - { - first: "string", - last: {type: "string"}, - fullName: { - get: function(){ - return this.first+" "+this.last; - } - }, - age: {value: 0}, - }); - -var me = new Person({first: "Justin", last: "Meyer"}) -me.fullName //-> "Justin Meyer" -me.age //-> 0 -``` - - -1. __name__ {String}: - Provides an optional name for this type that will - show up nicely in debuggers. - -1. __static__ {Object}: - Static properties that are set directly on the - constructor function. - -1. __prototype__ {Object\}: - A definition of the properties or methods on this type. - - If the property definition is a __plain function__, it's considered a method. - - ```js - var Person = DefineMap.extend({ - sayHi: function(){ console.log("hi"); } - }); - - var me = new Person(); - me.sayHi(); - ``` - - If the property definition is a __string__, it's considered a `type` setting to be looked up in [can.define.types](#types-object). - - ```js - var Person = DefineMap.extend({ - age: 'number', - isCool: 'boolean', - hobbies: 'observable' - }); - - var me = new Person({age: '33', isCool: 'false', hobbies: ['js','bball']}); - me.age //-> 33 - me.isCool //-> false - me.hobbies instanceof DefineList //-> true - ``` - - - If the property definition is a Constructor function, it's considered a `Type` setting. - - ```js - var Address = DefineMap.extend({ - zip: 'number' - }); - var Person = DefineMap.extend({ - address: Address - }); - - var me = new Person({address: {zip: '60048'}}); - me.address.zip //-> 60048 - ``` - - If the property is an __object__, it's considered to be a [propDefinition](#propdefinition-object). - - ```js - var Person = DefineMap.extend({ - fullName: { - get: function() { - return this.first+" "+this.last; - }, - set: function(newVal) { - var parts = newVal.split(" "); - this.first = parts[0]; - this.last = parts[1]; - } - }, - // slick way of creating an 'inline' type. - address: { - Type: { - zip: "number" - } - } - }); - - var me = new Person({fullName: "Rami Myer", address: {zip: '60048'}}); - me.first //-> "Rami" - me.address.zip //-> 60048 - ``` - - -- __returns__ {[can-define/map/map](#new-candefinemapprops)}: - A DefineMap constructor function. - -#### seal `{Boolean}` - -Defines if instances of the map should be [sealed](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) in development. - - - -##### Boolean -If `true`, in development, instances of this object will be [sealed](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal). In [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) errors will be thrown when undefined properties are set. This is the default -behavior of [extended DefineMaps](#candefinemapextendname-static-prototype): - -```js -"use strict"; -var Person = can.DefineMap.extend({}); -var me = new Person(); -me.age = 33 //-> throws "TypeError: Can't add property age, object is not extensible" -``` - -If `false`, the object will not be sealed. This is the default behavior of -unextended [DefineMaps](#new-candefinemapprops). Use [get](#mapgetpropname) and [set](#mapsetpropname-value) to get and set values: - -```js -var person = new can.DefineMap(); -person.set("first","Justin"); -person.set("last","Meyer"); - -person.get("first") //-> "Justin" -person.get("last") //-> "Meyer" -``` - -Set `seal` to `false` on objects that have an indeterminate number of properties: - -```js -var Style = can.DefineMap.extend({ - seal: false -},{ - cssText: { - get: function(){ - return _.map(this.get(), function(val, prop){ - return prop+": "+val; - }).join(";") - } - } -}); -var style = new Style(); -style.set("color","green"); -style.set("font","awesome"); -style.cssText //-> "color:green; font: awesome;" -``` - - -#### map.each( callback(item, propName ) ) - - -`each` iterates through the Map, calling a function -for each property value and key. - - -1. __callback__ {function(item, propName)}: - the function to call for each property - The value and key of each property will be passed as the first and second - arguments, respectively, to the callback. If the callback returns false, - the loop will stop. - - -- __returns__ {can.Map}: - this Map, for chaining - - -#### map.toObject() - - - -#### map.serialize() - - - -#### map.get(propName) - - - -#### map.set(propName, value) - - - -## __can-define/list/list__ function -Create observable list. - - -### new can.DefineList([items]) - - - -#### map.item(index, [newVal]) - - - -#### map.items() - - - -#### list.each( callback(item, index) ) - - -`each` iterates through the Map, calling a function -for each element. - - -1. __callback__ {function(*, Number)}: - the function to call for each element - The value and index of each element will be passed as the first and second - arguments, respectively, to the callback. If the callback returns false, - the loop will stop. - - -- __returns__ {can.DefineList}: - this DefineList, for chaining - - -#### list.forEach(callback[, thisArg]) - - -1. __callback__ {function(element, index, list)}: - a function to call with each element of the DefineList - The three parameters that _callback_ gets passed are _element_, the element at _index_, _index_ the - current element of the list, and _list_ the DefineList the elements are coming from. -1. __thisArg__ {Object}: - the object to use as `this` inside the callback - - -#### list.splice(index[, howMany[, ...newElements]]) - - -1. __index__ {Number}: - where to start removing or inserting elements - -1. __howMany__ {Number}: - the number of elements to remove - If _howMany_ is not provided, `splice` will remove all elements from `index` to the end of the DefineList. - -1. __newElements__ {*}: - elements to insert into the DefineList - - -- __returns__ {Array}: - the elements removed by `splice` - - -#### list.replace(collection) - - -1. __collection__ {Array|can.DefineList|can.Deferred}: - the collection of new elements to use - If a [can.Deferred] is passed, it must resolve to an `Array` or `can.DefineList`. - The elements of the list are not actually removed until the Deferred resolves. - - -#### list.push(...elements) - - -`push` adds elements onto the end of a DefineList. - - -1. __elements__ {*}: - the elements to add to the DefineList - - -- __returns__ {Number}: - the new length of the DefineList - - -#### list.unshift(...elements) - - -`unshift` adds elements onto the beginning of a DefineList. - - -1. __elements__ {*}: - the elements to add to the DefineList - - -- __returns__ {Number}: - the new length of the DefineList - - -#### list.pop() - - -`pop` removes an element from the end of a DefineList. - - -- __returns__ {*}: - the element just popped off the DefineList, or `undefined` if the DefineList was empty - - -#### list.shift() - - -`shift` removes an element from the beginning of a DefineList. - - -- __returns__ {*}: - the element just shifted off the DefineList, or `undefined` if the DefineList is empty - - -#### list.indexOf(item) - - -`indexOf` finds the position of a given item in the DefineList. - - -1. __item__ {*}: - the item to find - - -- __returns__ {Number}: - the position of the item in the DefineList, or -1 if the item is not found. - - -#### list.join(separator) - - -`join` turns a DefineList into a string by inserting _separator_ between the string representations -of all the elements of the DefineList. - - -1. __separator__ {String}: - the string to seperate elements with - - -- __returns__ {String}: - the joined string - - -#### list.reverse() - - -`reverse` reverses the elements of the DefineList in place. - - -- __returns__ {can.DefineList}: - the DefineList, for chaining - - -#### list.slice([start[, end]]) - - -`slice` creates a copy of a portion of the DefineList. - +## Documentation -1. __start__ {Number}: - the index to start copying from +Read the [API docs on CanJS.com](https://canjs.com/doc/can-define.html). -1. __end__ {Number}: - the first index not to include in the copy - If _end_ is not supplied, `slice` will copy until the end of the list. +## Changelog +See the [latest releases on GitHub](https://github.com/canjs/can-define/releases). -- __returns__ {can.DefineList}: - a new `can.DefineList` with the extracted elements +## Contributing +The [contribution guide](https://github.com/canjs/can-define/blob/master/CONTRIBUTING.md) has information on getting help, reporting bugs, developing locally, and more. -#### list.concat(...args) +## License +[MIT](https://github.com/canjs/can-define/blob/master/LICENSE.md) -1. __args__ {Array|can.DefineList|*}: - Any number of arrays, Lists, or values to add in - For each parameter given, if it is an Array or a DefineList, each of its elements will be added to - the end of the concatenated DefineList. Otherwise, the parameter itself will be added. diff --git a/document.js b/document.js deleted file mode 100644 index 8666c5d..0000000 --- a/document.js +++ /dev/null @@ -1,18 +0,0 @@ -var bitDocs = require("bit-docs"); -var path = require("path"); -var readmeGenerate = require("bit-docs-generate-readme"); - -bitDocs( - path.join(__dirname, "package.json"), - { - debug: true, - readme: { - apis: "./docs/apis.json" - }, - generators: [readmeGenerate] - }).catch(function(e){ - - setTimeout(function(){ - throw e; - }, 1); - }); diff --git a/package.json b/package.json index 6ab2337..296b46c 100644 --- a/package.json +++ b/package.json @@ -50,20 +50,5 @@ "steal-tools": "^1.4.0", "testee": "^0.7.0", "bit-docs": "^0.0.7" - }, - "bit-docs": { - "dependencies": { - "bit-docs-glob-finder": "^0.0.5", - "bit-docs-dev": "^0.0.3", - "bit-docs-js": "^0.0.4" - }, - "glob": { - "pattern": "**/*.{js,md}", - "ignore": "node_modules/**/*" - }, - "parent": "can-define", - "readme": { - "apis": "./docs/apis.json" - } } -} +} \ No newline at end of file