Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to clone objects before merge. Fixes #28 issue. #44

Merged
merged 4 commits into from
Oct 12, 2016

Conversation

gagan-bansal
Copy link
Contributor

@gagan-bansal gagan-bansal commented Oct 2, 2016

Added option clone as boolean to clone objects before merge. Fixes #28 issue.

Copy link
Owner

@TehShrike TehShrike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty reasonable to me - I want to make sure we don't miss some case where we might be copying objects over though, so some tests with dates and arrays, and also some identical-looking objects would be good.

It might be simpler to assert src.a !== target.a and src.a.key !== target.a.key to assert that cloning was happening. since that's what I think we care about most - if it's not a primitive, we need to make sure that the value is a copy, and not the same object in memory.

@@ -31,15 +31,23 @@ function defaultArrayMerge(target, source, optionsArgument) {
}

function mergeObject(target, source, optionsArgument) {
var clone = optionsArgument && optionsArgument.hasOwnProperty('clone')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var clone = optionsArgument && optionsArgument.close === true is probably safe enough, since we're fine with defaulting to false.

merged.a.d = "bar-modifies"
t.equal(src.b.c, "foo")
t.equal(target.a.d, "bar")

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two tests should probably assert that merged deep equals expected, since the two "modified" values wouldn't conflict with each other.

var destination = {}
if (isMergeableObject(target)) {
Object.keys(target).forEach(function (key) {
destination[key] = target[key]
var val = target[key]
if (clone && typeof val === 'object')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing that this should be isMergeableObject(val) - but it would be good to have some tests covering dates/arrays first to make sure.

@TehShrike
Copy link
Owner

Think you'll get a chance to revisit this @gagan-bansal? I'm fine leaving this pull request open for a while if you're just waiting for the time to get back to it.

@gagan-bansal
Copy link
Contributor Author

@TehShrike I read your comments and all points are fine and need to be implemented. Because of time I could not do take up this. This weekend (ie today and tomorrow) I'll implement the required changes.

@TehShrike
Copy link
Owner

No worries. Whenever you get the chance. 👍

@gagan-bansal
Copy link
Contributor Author

I have rechecked and corrected as per the suggestion. Thanks for guidance.

Copy link
Owner

@TehShrike TehShrike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few more things I noticed.

Also, I think it would be reasonable to make a new function for the "empty target" pattern used here:

function emptyTarget(val) {
  return Array.isArray(val) ? [] : {}
}

var src = { key1: /efg/ }
var expected = { key1: /efg/ }

t.deepEqual(merge(target, src, {clone: true}), expected)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deepEqual won't assert as much as we want, here - if this test is meant to assert that the original regex object is copied to the new object, we need to assert t.equal(target.key1, src.key1) (the expected object isn't actually what we expect, since we should expect the regular expression object to be copied directly instead of just being made up of the same pattern).

var actual = merge(target, source, {clone: true})

t.deepEqual(actual, expected)
t.equal(actual.key.valueOf(), tuesday.valueOf())
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the regex test - we need to assert that the objects are the same object in memory, not that their underlying values are the same. t.equal(target.key, tuesday) (no expected object needed).

var merged = merge(target, src, {clone: true})
t.deepEqual(merged, expected)
t.ok(Array.isArray(merge(target, src).key1))
t.ok(Array.isArray(merge(target, src).key2))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think these three assertions (deepEqual, and the two isArrays) are necessary for this test, we should have other tests covering that behavior. The cloning assertions below are all we should be asserting here.

@@ -18,28 +18,44 @@ function isMergeableObject(val) {

function defaultArrayMerge(target, source, optionsArgument) {
var destination = target.slice()
var clone = optionsArgument && optionsArgument.clone === true
source.forEach(function(e, i) {
if (typeof destination[i] === 'undefined') {
destination[i] = e
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think e needs to be cloned in this case, too - I think a failing test for this case might be

var a = { key: 'yup' }
var target = []
var source = [a]
var output = deepmerge(target, source)
t.notEqual(output[0], a)
t.equal(output[0].key, 'yup')

But I haven't tested that to make sure it fails right now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TehShrike Good analytical thinking.

@gagan-bansal
Copy link
Contributor Author

Improved the test cases.

@TehShrike
Copy link
Owner

Looks good to me!

@TehShrike
Copy link
Owner

Note to self: check after merging, but I don't think that source.slice will be sufficient (or have test coverage) to clone. https://github.com/KyleAMathews/deepmerge/pull/46/files

@TehShrike TehShrike merged commit a4942bb into TehShrike:master Oct 12, 2016
@gagan-bansal
Copy link
Contributor Author

@TehShrike Yes slice is shallow copy. We can change it to

// current
   return Array.isArray(target) ? arrayMerge(target, source, optionsArgument) : 
     source.slice()
// can be
  if (clone) source = deepmerge([], source)
   return Array.isArray(target) ? arrayMerge(target, source, optionsArgument) : source

@TehShrike
Copy link
Owner

I opened a pull request with a fix at #48. I'll push this out as a feature release once it gets merged.

TehShrike added a commit that referenced this pull request Oct 14, 2016
The issue was noted in #44 (comment)
@TehShrike
Copy link
Owner

Published as 1.2.0

aretecode pushed a commit to fluents/chain-able that referenced this pull request Jun 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants