• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alinex / node-util / 232

pending completion
232

push

travis-ci

alinex
Use normal time method in tests.

92 of 495 relevant lines covered (18.59%)

0.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

9.52
/src/mod/extend.coffee
1
###
2
Extend Object
3
=================================================
4
Extend an object with another one.
5

6
This method will extend a given object with the entries from additional
7
objects. Therefore it will do a deep extend.
8

9
__Example:__
10

11
``` coffee
12
util = require 'alinex-util'
13
test = { eins: 1 }
14
util.extend test, { zwei: 2 }, { eins: 'eins' }, { drei: 3 }
15
```
16

17
This results to:
18

19
``` coffee
20
test = { zwei: 2, eins: 'eins', drei: 3 }
21
```
22

23
But keep in mind that this will change the first object, to the result, too.
24
###
25

26

27
# Node modules
28
# -------------------------------------------------
29
debug = require('debug')('util:extend')
2✔
30
util = require 'util'
2✔
31
chalk = require 'chalk'
2✔
32
# internal modules
33
clone = require './clone'
2✔
34

35

36
# Setup
37
# -------------------------------------------------
38
# Because the function works synchrone, the indent variable can be used accross
39
# all calls to show the level in the debug messages. It is set to 3 spaces for
40
# the initial call increased by 3 every subcall.
41
indent = ''
2✔
42

43

44
# External Methods
45
# -------------------------------------------------
46

47
###
48
@param {String} [mode] containing one or multiple keys which have to be separated
49
by space or comma:
50
- `CLONE` - clone object and all extenders before extending, keeps the resulting
51
  objects untouched (works only globally)
52
- `OVERWRITE` - allow overwrite of array and object mode in specific elements
53
- `ARRAY_CONCAT` - (default) if no other array-mode set, will concat additional
54
  elements
55
- `ARRAY_REPLACE` - for all arrays, replace the previouse array completely instead
56
  of extending them
57
- `ARRAY_OVERWRITE` - overwrite the same index instead of extending the array
58
- `OBJECT_EXTEND` - (default) if no other object-mode given, will add/replace
59
  properties with the new ones
60
- `OBJECT_REPLACE` - will always replace the object completely with the new one,
61
  if the keys are different
62
@param {Object} obj object to be extended
63
@param {Object} [ext...] extenders to be applied to the base one
64
@return {Object} new combined object
65
@description
66
The `mode` may also be changed on any specific element by giving a different mode
67
just for this operation in the extending element itself. Therefore an array
68
should has the mode as first element or an object as an attribute.
69
###
70
module.exports = extend = (ext...) ->
2✔
71
  indent += '   '
×
72
  # read mode
73
  mode = []
×
74
  if typeof ext[0] is 'string' and ext[0]?.indexOf 'MODE' is 0
×
75
    mode = ext.shift().split(' ')[1..]
×
76
  # return if no object given
77
  return null unless ext.length
×
78
  # clone all if defined, and remove flag
79
  ext = clone ext if 'CLONE' in mode
×
80
  mode = mode.filter (e) -> e isnt 'CLONE'
×
81
  # run over extenders
82
  obj = ext.shift()
×
83
  debug "#{indent[3..]}-> extend #{chalk.grey util.inspect obj}"
×
84
  debug "#{indent[3..]}   using mode: #{mode.join ', '}" if mode.length
×
85
  # use all extenders
86
  for src in ext
×
87
    continue unless src?
×
88
    # empty source object
89
    continue if src.constructor?.name is Object.name and not Object.keys(src).length
×
90
    debug "#{indent[3..]}   by #{chalk.grey util.inspect src}"
×
91
    # undefined object
92
    unless obj?
×
93
      obj = src
×
94
      continue
×
95
    # arrays
96
    if Array.isArray src
×
97
      # check for changed mode
98
      cmode = mode
×
99
      if 'OVERWRITE' in mode and typeof src[0] is 'string' and src[0][0..4] is 'MODE '
×
100
        cmode = src.shift().split(' ')[1..]
×
101
        debug "#{indent[3..]}   change mode: #{cmode.join ', '}"
×
102
      else mode
×
103
      # extend depending on mode
104
      if 'ARRAY_REPLACE' in cmode or not Array.isArray obj
×
105
        obj = src
×
106
        continue
×
107
      if 'ARRAY_OVERWRITE' in cmode
×
108
        obj[i] = extend "MODE #{mode.join ' '}", obj[i], n for n, i in src
×
109
        continue
×
110
      # default setting ARRAY_CONCAT
111
      obj.push n for n in src
×
112
      continue
×
113
    # all other
114
    unless typeof src is 'object'
×
115
      obj = src
×
116
      continue
×
117
    # this is a literal
118
    # check for changed mode
119
    cmode = mode
×
120
    if 'OVERWRITE' in mode and src.OBJECT_REPLACE
×
121
      cmode = ['OBJECT_REPLACE']
×
122
      delete src.OBJECT_REPLACE
×
123
      debug "#{indent[3..]}   change mode: #{cmode.join ', '}"
×
124
    # replace if different type
125
    if not(obj?) or Array.isArray(obj) or typeof obj isnt 'object' or
×
126
    obj.constructor.name isnt Object.name
127
      obj = src
×
128
      continue
×
129
    # replace object
130
    if 'OBJECT_REPLACE' in cmode and Object.keys(obj).join(',') isnt Object.keys(src).join(',')
×
131
      obj = src
×
132
      continue
×
133
    # extend based on OBJECT_EXTEND mode
134
    for own k, v of src
×
135
      if v is null
×
136
        delete obj[k]
×
137
        continue
×
138
      # test to assure a key like 'toString' won't map to the standard function
139
      obj[k] = if k in Object.keys(obj)
×
140
        if mode.length
×
141
          extend "MODE #{mode.join ' '}", obj[k], v
×
142
        else
143
          extend obj[k], v
×
144
      else
145
        v
×
146
  # return resulting obj
147
  indent = indent[3..]
×
148
  debug "#{indent}<- #{chalk.grey util.inspect obj}"
×
149
  obj
×
150

151
###
152
Mode Settings
153
-----------------------------------------------------------
154
You may set a mode globally or in a specific level like described in the method
155
definition.
156

157
See the example below which replaces arrays instead of appending to them (the default).
158

159
``` coffee
160
test1 = {a: [1, 2, 3], b: [1, 2, 3], c: [1, 2, 3]}
161
test2 = {a: [4, 5, 6], c: ['a']}
162
ext = util.extend 'MODE ARRAY_REPLACE', test1, test2
163
# ext = {a: [4, 5, 6], b: [1, 2, 3], c: ['a']}
164
```
165

166
And you may also change the mode only for one element addition (here the first array):
167

168
``` coffee
169
test1 = {a: [1, 2, 3], b: [1, 2, 3], c: [1, 2, 3]}
170
test2 = {a: ['MODE ARRAY_REPLACE', 4, 5, 6], c: ['a']}
171
ext = util.extend test1, test2
172
# ext = {a: [4, 5, 6], b: [1, 2, 3], c: [1, 2, 3, 'a']}
173
```
174

175
And to set a mode in an object you give it as argument with value `true`:
176

177
``` coffee
178
test1 = {t1: {a: 1, b: 2, c: 3}, t2: {d: 4, e: 5, f: 6}}
179
test2 = {t1: {OBJECT_REPLACE: true, a: 4, b: 5}, t2: {d: 9}}
180
ext = util.extend test1, test2
181
# ext = {t1: {a: 4, b: 5}, t2: {d: 9, e: 5, f: 6}}
182
```
183

184

185
Debugging
186
--------------------------------------------------
187
Debugging is possible using environment setting:
188

189
```
190
DEBUG=util:extend    -> shows each level of cloning
191
```
192

193
    util:extend -> extend { eins: 1 } +0ms
194
    util:extend    by { zwei: 2 } +1ms
195
    util:extend    by { eins: 'eins' } +0ms
196
    util:extend    -> extend 1 +0ms
197
    util:extend       by 'eins' +0ms
198
    util:extend    <- 'eins' +0ms
199
    util:extend    by { drei: 3 } +0ms
200
    util:extend <- { eins: 'eins', zwei: 2, drei: 3 } +0ms
201
###
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc