patternjavascriptMinor
Object wrapper/parameter injection module
Viewed 0 times
modulewrapperinjectionobjectparameter
Problem
I made an object wrapper/parameter injector and I hope it will be useful. I made it for a project I am working on but then decided to try polishing it a bit.
The use case is for wrapping objects such that you can inject parameters into their methods by calling the wrapper as a function. I was heavily inspired by AngularJS and their use of parameter injection to almost create a DSL with their framework.
Questions:
Repo
negotiator.js
```
# A small tool for proxying objects behind a wrapper that can inject
# parameters into their methods.
# Extract parameter names from a function,
# position is given by position this array.
if module
_ = require('underscore');
else
_ = window._
utils = {};
utils.parameterNames = (func) ->
funcArgs = func.toString().split('(')[1].split(')')[0].split(',')
return (k.trim() for k in funcArgs)
# Inject parameters from a context object into a function.
# Passed parameters are supplied by the 'params' argument.
utils.injectAndApply =(func, parameters, context, target) ->
signature = utils.parameterNames func
parameters = [] if _.isEmpty(parameters)
for position, name of signature
parameters[position] = context[name] if context[name]?
parameters.length = signature.length;
return func.apply target ? {}, parameters
# Constructor for proxy object.
utils.Proxy = (real) ->
@$real = real
self = this
for key, method of real when typeof method is 'function'
do (method, key) ->
self[key] = ->
utils.injectAndApply method, arguments, @$context ? {}, @$real
return this
# Build a context object from an arguments list.
utils.buildContextFromParams = (func, parameters) ->
signature = utils.parameterNames func
context = {}
for key, value of parameters
context[signature[key]] = value
return context
# This is the circular wra
The use case is for wrapping objects such that you can inject parameters into their methods by calling the wrapper as a function. I was heavily inspired by AngularJS and their use of parameter injection to almost create a DSL with their framework.
Questions:
- Is this a well-designed module?
- Is my code legible?
- Am I over looking something that might lead to bugs later?
- Is something like this safe to use?
Repo
negotiator.js
```
# A small tool for proxying objects behind a wrapper that can inject
# parameters into their methods.
# Extract parameter names from a function,
# position is given by position this array.
if module
_ = require('underscore');
else
_ = window._
utils = {};
utils.parameterNames = (func) ->
funcArgs = func.toString().split('(')[1].split(')')[0].split(',')
return (k.trim() for k in funcArgs)
# Inject parameters from a context object into a function.
# Passed parameters are supplied by the 'params' argument.
utils.injectAndApply =(func, parameters, context, target) ->
signature = utils.parameterNames func
parameters = [] if _.isEmpty(parameters)
for position, name of signature
parameters[position] = context[name] if context[name]?
parameters.length = signature.length;
return func.apply target ? {}, parameters
# Constructor for proxy object.
utils.Proxy = (real) ->
@$real = real
self = this
for key, method of real when typeof method is 'function'
do (method, key) ->
self[key] = ->
utils.injectAndApply method, arguments, @$context ? {}, @$real
return this
# Build a context object from an arguments list.
utils.buildContextFromParams = (func, parameters) ->
signature = utils.parameterNames func
context = {}
for key, value of parameters
context[signature[key]] = value
return context
# This is the circular wra
Solution
Looks quite good.
Nevertheless here are some thoughts:
Parameter Names
You might get problems when calling
Moreover you'll get an array with one entry (
You could change your implementation to this:
Here is an other implementation (source) using an regular expression
We could also write s.th. like this:
The performance depend on the browser but the last one seems to be pretty fast (see benchmarks)
Expressions
In CoffeeScript every thing is an expression so you can turn this
into this
Iteration
This:
could also be written in that way:
or even:
It compiles all to the same JS. I'd prefer the second variant.
Nevertheless here are some thoughts:
Parameter Names
You might get problems when calling
utils.parameterNames without arguments. E.g. func.toString() would fail if func is undefined.Moreover you'll get an array with one entry (
['']) if you pass a function with no arguments (utils.parameterNames(->)) where you might expect an empty array.You could change your implementation to this:
utils.parameterNames = (func=->) ->
funcArgs = func.toString().split('(')[1].split(')')[0].split(',')
(t for k in funcArgs when (t=k.trim()) isnt '')Here is an other implementation (source) using an regular expression
getArgumentNames = (fn=->) ->
args = fn.toString().match ///
function # start with 'function'
[^(]* # any character but not '('
\( # open bracket = '(' character
([^)]*) # any character but not ')'
\) # close bracket = ')' character
///
return [] if not args? or (args.length < 2)
args = args[1]
args = args.split /\s*,\s*/
(a for a in args when a.trim() isnt '')We could also write s.th. like this:
fnRgx = ///
function # start with 'function'
[^(]* # any character but not '('
\( # open bracket = '(' character
([^)]*) # any character but not ')'
\) # close bracket = ')' character
///
argRgx = /([^\s,]+)/g
parameterNames = (fn) ->
(fn?.toString().match(fnRegex)?[1] or '').match(argRgx) or []The performance depend on the browser but the last one seems to be pretty fast (see benchmarks)
Expressions
In CoffeeScript every thing is an expression so you can turn this
if module
_ = require('underscore');
else
_ = window._into this
_ = if module then require('underscore') else window._Iteration
This:
for position, name of signature
parameters[position] = context[name] if context[name]?could also be written in that way:
for position, name of signature when context[name]?
parameters[position] = context[name]or even:
parameters[position] = context[name] for position, name of signature when context[name]?It compiles all to the same JS. I'd prefer the second variant.
Code Snippets
utils.parameterNames = (func=->) ->
funcArgs = func.toString().split('(')[1].split(')')[0].split(',')
(t for k in funcArgs when (t=k.trim()) isnt '')getArgumentNames = (fn=->) ->
args = fn.toString().match ///
function # start with 'function'
[^(]* # any character but not '('
\( # open bracket = '(' character
([^)]*) # any character but not ')'
\) # close bracket = ')' character
///
return [] if not args? or (args.length < 2)
args = args[1]
args = args.split /\s*,\s*/
(a for a in args when a.trim() isnt '')fnRgx = ///
function # start with 'function'
[^(]* # any character but not '('
\( # open bracket = '(' character
([^)]*) # any character but not ')'
\) # close bracket = ')' character
///
argRgx = /([^\s,]+)/g
parameterNames = (fn) ->
(fn?.toString().match(fnRegex)?[1] or '').match(argRgx) or []if module
_ = require('underscore');
else
_ = window.__ = if module then require('underscore') else window._Context
StackExchange Code Review Q#11891, answer score: 3
Revisions (0)
No revisions yet.