HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Object wrapper/parameter injection module

Submitted by: @import:stackexchange-codereview··
0
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:

  • 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 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.