Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolae Claudius committed Jul 23, 2011
0 parents commit 9e3e3a6
Show file tree
Hide file tree
Showing 11 changed files with 287 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules
77 changes: 77 additions & 0 deletions README.md
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,77 @@
### About

Meta Code is a set of metaprogramming utilities for CoffeeScript (inspired by Ruby).

Although you can achieve the same things by hard-coding evrything, this package is good
because function names convey meaning, and is good to have consensus among developers.


### Features

Curently, the folowing tools are provided (nicely documented in the code)

* **module**
- `extend` - copies the module object's methods onto an object
- `include` - copies the module object's methods onto an object's prototype
- `includeInter` - inject a shallow copy of the module object into an object's prototype chain

* **forward**
- `forward` - forward a method call to an object property

* **autoload**
- `autoload` - load a property from a file


### Install

Install with npm:

```shell
# install locally in "./node_modules"
npm install meta_code

# or use -g to install globally
npm install meta_code -g

# or add it as a dependecy to your package.json and run
npm install
```


### Usage

Metacode consists of a series of tools like "forward" or "module" that aid you to inject code in your objects.
There are two interfaces that can be used:

1. Use via `metaCode` helper

```coffeescript
metaCode = require 'meta_code'

# a "module" we'll be including in our class
Power =
sword: 'katana'
fight: -> console.log @sword

class Samurai
# Enable the module tool. Once enabled we can use this tool's methods
# like 'extend' and 'include' in our object, because the tool's methods
# get copied in our object
metaCode @, 'module'
@include Power
```

2. Use directly

```coffeescript
module = require 'meta_code/tools/module' # include the tool

# a "module" we'll be including in our class
Power =
sword: 'katana'
fight: -> console.log @sword

class Samurai
module.include.call @, Power
```

33 changes: 33 additions & 0 deletions index.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,33 @@
# A set of metaprogramming tools to aid development, inspired by Ruby


# Use this function to enable a set of metacode tools in your class
#
# class Controller
# metaCode @, 'forward'
# @forward 'req', 'param', 'session'
#
# c = new Controller()
# c.req = someReq
# c.param 'x' # forwards call to "req" object
#
# @object {Object} the class (in CoffeeScript sense) to be augmented
# @modules {Strings...} the modules to bring in
# @api public
metaCode = (object, tools...) ->
for toolName in tools
tool = require metaCode.loadPath + toolName
object[method] = tool[method] for own method of tool


# Default load path, can be customized for custom needs or testing
# Example values
#
# "./tools/"
# __dirname + "/tools"
#
metaCode.loadPath = "./tools/"


module.exports = metaCode

26 changes: 26 additions & 0 deletions package.json
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"author": "Nicolae Claudius",
"name": "meta_code",
"description": "Metaprogramming utilities for CoffeeScript",
"version": "0.0.1",
"keywords": ["metaprogramming", "coffeescript"],
"repository": {
"type": "git",
"url": "git://github.com/clyfe/meta_code.git"
},
"main": "index",
"scripts": {
"test": "expresso"
},
"engines": {
"node": "~v0.4.9"
},
"dependencies": {
"coffee-script": ">= 1.1.1",
"underscore": ">= 1.1.7"
},
"devDependencies": {
"expresso": ">= 0.8.1"
}
}

10 changes: 10 additions & 0 deletions test/autoload.test.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,10 @@
assert = require 'assert'
metaCode = require '../index'

# test the forward tool
exports.autoload = ->
class C
metaCode @, 'autoload'
@autoload 'sample_module', __dirname + '/autoload/sample_module'
assert.equal C.sample_module, 1

1 change: 1 addition & 0 deletions test/autoload/sample_module.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 1
16 changes: 16 additions & 0 deletions test/forward.test.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,16 @@
assert = require 'assert'
metaCode = require '../index'

# test the forward tool
exports.forward = ->
property =
method1: -> 'method1'
method2: -> 'method2'
class C
metaCode @, 'forward'
@forward 'property', 'method1', 'method2'
constructor: (@property) ->
c = new C(property)
assert.equal c.method1(), 'method1'
assert.equal c.method2(), 'method2'

9 changes: 9 additions & 0 deletions test/meta_code.test.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,9 @@
assert = require 'assert'
metaCode = require '../index'

# test the metacode tool importer
exports.metaCode = ->
class C
metaCode @, 'forward'
assert.ok C.forward

17 changes: 17 additions & 0 deletions tools/autoload.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,17 @@
# Forward method calls to certain properties.
#
# class I18n
# metaCode @, 'autoload'
# @autoload 'Backend', 'lib/backend'
#
# console.log I18n.Backend
#
# @name {String} the name of the property onto wich we load
# @methods {path} the path to be required
# @api public
autoload = (name, path) ->
@__defineGetter__ name, -> require path


exports.autoload = autoload

24 changes: 24 additions & 0 deletions tools/forward.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,24 @@
# Forward method calls to certain properties.
#
# class Controller
# metaCode @, 'forward'
# @forward 'req', 'param', 'session'
#
# same as
#
# class Controller
# param: -> @req.param.apply @req, arguments
# session: -> @req.session.apply @req, arguments
#
# @object {String} the name of the property to forward calls to
# @methods {Strings...} the methods to be wired
# @api public
forward = (property, methods...) ->
proto = @::
for method in methods
do (method) ->
proto[method] = -> @[property][method].apply @[property], arguments


exports.forward = forward

73 changes: 73 additions & 0 deletions tools/module.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,73 @@
_ = require 'underscore'


# Copy into object all properties from module
#
# Macros =
# delegate: (to, what) ->
# @[what] = @[to][what].apply @[to], arguments
#
# class Application extends Controller
# metaCode @, 'module'
# @extend Macros
# @delegate 'req', 'userId'
# index: -> console.log @userId()
#
# @objects {Objects...} the objects to extend with
# @api public
extend = (objects...) ->
for object in objects
for name, property of object
@[name] = property


# Copy into object all properties from module onto the prototype
#
# Authentication =
# currentUser: (cb) ->
# User.find @session('userId'), (err, user) ->
# user = new User() if err
# cb(user)
#
# Authorization =
# authorize: (role, cb) ->
# @currentUser (user) ->
# throw new Error('Not Authorized') unless user.role == role
# cb()
#
# class Tweets extends Controller
# metaCode @, 'module'
# @include Authentication, Authorization
# show:
# @authorize 'admin', =>
# Tweet.find @params 'id', (@err, @tweet) => @render 'show'
#
# @objects {Objects...} the objects to mix in
# @api public
include = (objects...) ->
proto = @::
for object in objects
for own name, property of object
proto[name] = property


# The same as "include" only it does not copy the properties, it achieves
# augmentation by injecting copies of module-objects into the receiver's
# prototype chain
#
# @objects {Objects...} the objects to mix in
# @api public
includeInter = (objects...) ->
for object in objects
module = _.clone(object)
previousPrototype = @::
@:: = module
module.__proto__ = previousPrototype
# make sure to preserve the constructor property
@::constructor = previousPrototype.constructor if previousPrototype.hasOwnProperty 'constructor'


exports.extend = extend
exports.include = include
exports.includeInter = includeInter

0 comments on commit 9e3e3a6

Please sign in to comment.