Skip to content

Commit

Permalink
Merge pull request #170 from offirgolan/underscore-super
Browse files Browse the repository at this point in the history
Only call super on validations class if it exists via shouldCallSuper
  • Loading branch information
offirgolan committed Apr 16, 2016
2 parents e9f15bb + 4dcb031 commit aeccc03
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 6 deletions.
27 changes: 27 additions & 0 deletions addon/utils/should-call-super.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright 2016, Yahoo! Inc.
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/

/**
* Checks if the give key exists on the object's super.
* If so, we can successfuly call the obj[key] _super
*
* Created by @rwjblue
*/
export default function shouldCallSuper(obj, key) {
let current = Object.getPrototypeOf(obj);
current = Object.getPrototypeOf(current);

while (current) {
let descriptor = Object.getOwnPropertyDescriptor(current, key);

if (descriptor) {
return true;
}

current = Object.getPrototypeOf(current);
}

return false;
}
5 changes: 5 additions & 0 deletions addon/utils/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* Copyright 2016, Yahoo! Inc.
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/

export function hasEmberData() {
return typeof self.DS !== 'undefined';
}
32 changes: 26 additions & 6 deletions addon/validations/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ValidationResult from './result';
import ValidationResultCollection from './result-collection';
import BaseValidator from '../validators/base';
import cycleBreaker from '../utils/cycle-breaker';
import shouldCallSuper from '../utils/should-call-super';

const {
get,
Expand Down Expand Up @@ -95,17 +96,30 @@ default
function buildValidations(validations = {}, globalOptions = {}) {
normalizeOptions(validations, globalOptions);

let Validations;
let Validations, validationMixinCount;

return Ember.Mixin.create({
_validationsClass: computed(function () {
init() {
this._super(...arguments);

// Count number of mixins to bypass super check if there is more than 1
this.__validationsMixinCount__ = this.__validationsMixinCount__ || 0;
validationMixinCount = ++this.__validationsMixinCount__;
},
__validationsClass__: computed(function () {
if (!Validations) {
Validations = createValidationsClass(this._super(), validations);
let inheritedClass;
console.log(shouldCallSuper(this, '__validationsClass__'), validationMixinCount);
if(shouldCallSuper(this, '__validationsClass__') || validationMixinCount > 1) {
inheritedClass = this._super();
}

Validations = createValidationsClass(inheritedClass, validations);
}
return Validations;
}).readOnly(),
validations: computed(function () {
return this.get('_validationsClass').create({
return this.get('__validationsClass__').create({
model: this
});
}).readOnly(),
Expand Down Expand Up @@ -181,7 +195,7 @@ function createValidationsClass(inheritedValidationsClass, validations = {}) {
let validatableAttributes = Object.keys(validations);

// Setup validation inheritance
if (inheritedValidationsClass) {
if (inheritedValidationsClass && inheritedValidationsClass.__isCPValidationsClass__) {
const inheritedValidations = inheritedValidationsClass.create();

validationRules = merge(validationRules, inheritedValidations.get('_validationRules'));
Expand Down Expand Up @@ -235,7 +249,7 @@ function createValidationsClass(inheritedValidationsClass, validations = {}) {
});

// Create `validations` class
return Ember.Object.extend(TopLevelProps, {
const ValidationsClass = Ember.Object.extend(TopLevelProps, {
model: null,
attrs: null,
isValidations: true,
Expand Down Expand Up @@ -286,6 +300,12 @@ function createValidationsClass(inheritedValidationsClass, validations = {}) {
});
}
});

ValidationsClass.reopenClass({
__isCPValidationsClass__: true
});

return ValidationsClass;
}

/**
Expand Down
67 changes: 67 additions & 0 deletions tests/integration/validations/factory-general-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -650,3 +650,70 @@ test("nested keys - inheritance", function(assert) {
});


test("call super in validations class with no super property", function(assert) {
// see https://github.com/offirgolan/ember-cp-validations/issues/149
assert.expect(1);

var Validations = buildValidations({
'firstName': validator(Validators.presence)
});

var mixin = Ember.Mixin.create({
actions: {
foo() {
// The issue is that __validationsClass__ is calling super but since
// this is no __validationsClass__ method on the super, it is wrapped
// with the closest context which is the 'foo' action
assert.ok(false); // should not reach here
}
}
});

var component = setupObject(this, Ember.Component.extend(Validations, mixin, {
actions: {
foo() {
assert.ok(true);
const validations = this.get('validations');
}
}
}));

component.send('foo');
});


test("multiple mixins", function(assert) {
var Validations1 = buildValidations({
'firstName': validator(Validators.presence)
});

var Validations2 = buildValidations({
'middleName': validator(Validators.presence)
});

var Validations3 = buildValidations({
'lastName': validator(Validators.presence)
});

var object = setupObject(this, Ember.Object.extend(Validations1, Validations2, Validations3));

assert.deepEqual(object.get('validations.validatableAttributes').sort(), ['firstName', 'middleName', 'lastName'].sort());
assert.equal(object.get('validations.attrs.firstName.isValid'), false);
assert.equal(object.get('validations.attrs.lastName.isValid'), false);
assert.equal(object.get('validations.isValid'), false);

object.set('firstName', 'Offir');

assert.equal(object.get('validations.attrs.firstName.isValid'), true);
assert.equal(object.get('validations.isValid'), false);

object.set('middleName', 'David');

assert.equal(object.get('validations.attrs.middleName.isValid'), true);
assert.equal(object.get('validations.isValid'), false);

object.set('lastName', 'Golan');

assert.equal(object.get('validations.attrs.lastName.isValid'), true);
assert.equal(object.get('validations.isValid'), true);
});
37 changes: 37 additions & 0 deletions tests/unit/utils/should-call-super-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Ember from 'ember';
import { module, test } from 'qunit';
import shouldCallSuper from 'ember-cp-validations/utils/should-call-super';

const {
computed
} = Ember;

module('Unit | Utils | shouldCallSuper');

test('shouldCallSuper - true', function(assert) {
const Parent = Ember.Object.extend({
foo: computed(function() {})
});

const Child = Parent.extend({
foo: computed(function() {
assert.ok(shouldCallSuper(this, 'foo'));
})
});

const child = Child.create();
child.get('foo');
});

test('shouldCallSuper - false', function(assert) {
const Parent = Ember.Object.extend();

const Child = Parent.extend({
foo: computed(function() {
assert.ok(!shouldCallSuper(this, 'foo'));
})
});

const child = Child.create();
child.get('foo');
});

0 comments on commit aeccc03

Please sign in to comment.