Jasmine test for javascript getter not working - javascript

I'm writing some test for for an angularjs factory and some of the expectations are not working and I really don't know why.
This is my factory (part of it).
'use strict';
angular.module('myAppMod')
.factory('Person', function(BaseModel) {
return BaseModel.extend({
get fullname() {
var name = [];
if (this.first_name) {
name.push(this.first_name);
}
if (this.person_extra && this.person_extra.middle_name) {
name.push(this.person_extra.middle_name);
}
if (this.last_name) {
name.push(this.last_name);
}
return name.join(' ');
}
});
});
and Jasmine tests:
var p;
beforeEach(function() {
p = new Person({
first_name: 'first_name',
person_extra: {
middle_name: 'middle_name',
media_item_id: null
},
last_name: 'last_name',
security_level: 'security_level'
}, true);
});
it("has a fullname", function() {
expect(p.fullname).toEqual('first_name middle_name last_name');
});
p.fullnameis returning ""(empty string) and in the factory, console.log(this.first_name), is undefined.
Any help is really appreciated.
Thank you in advance

EDIT: After further investigation, I have changed my answer.
It is not working because you are using the getter shorthand (get fnName() { }) through the extend method. The getter's this is the anonymous object itself and does not inherit the methods and properties of the Backbone model, whereas the this in function properties do. I have made a codepen that illustrate your problem.
That is, if this is your Model
var Model = BaseModel.extend({
get isBackboneModelThroughGetter() {
return !!this.get;
},
isBackboneModel: function() {
return !!this.get;
},
});
Then an instance of Model will make this test pass:
it('should make you wonder', function() {
var model = new Model();
expect(model.isBackboneModel()).toBe(true);
expect(model.isBackboneModelThroughGetter).not.toBe(true);
});
Thus, to make your Person factory work, you will need:
To replace every property access by the proper Backbone call: this.get('propertyName') instead of this.propertyName
Replace all getters by function properties: full_name : function() { /*...*/ } instead of get full_name() { /* ... */ }
Replace calls to model.full_name by model.full_name();

I assume that you're using the built-in angular.extend. angular.extend does not copy getters and setters. There's been an open issue on GitHub on this specific subject since the 12th of August 2014.
As for why it still isn't implemented:
Angular exposes some of the helper functions that it uses internally. This is the case for extend, copy and many others. There are other libraries that specialize in these functions, keep their
focus is there and can do a better job.
It is not in the best interest of most users to make these helper functions big nor slow, as these are used internally and any change in that direction can have a direct impact in download size and performance. At the same time, apps that need the most accurate version, should be better served with other libraries.
There are many ways to solve this issue. decaf.js provides an example implementation that should work for most cases. GitHub is probably a better environment to dive into their code, but it comes down to this:
function extend (me) {
var args = Array.prototype.slice.call(arguments, 1);
decaf.each(args, function (o) {
for (var key in o) {
if (o.hasOwnProperty(key)) {
var desc = Object.getOwnPropertyDescriptor(o, key);
var g = desc.get;
var s = desc.set;
if (g || s) {
Object.defineProperty(me, key, { get: g, set: s, enumerable: true });
} else {
me[key] = o[key];
}
}
}
});
return me;
}

Related

ES6: Access two different scopes (class-global and object-local) in the same function

This is kind of hard to explain. Please excuse the farfetched example.
I want to access two different scopes (class-global and object-local) in the same function. Said function is an object-method.
I'm using babel-preset-react-native, which should be ES6 (https://facebook.github.io/react-native/docs/javascript-environment.html).
Given the following example-class:
class Logic {
constructor() {
this.keys = ['Red Key', 'Blue Key'];
}
hasKey = (searchKey) => {
return this.keys.includes(searchKey);
}
allowedToEnter = {
redHouse: () => {
// some logic
return true;
},
// ...
};
houses = [
{
name: 'Red House',
isAccessible: () => {
return this.hasKey('Red Key');
},
shouldAccess__Global: () => {
return /* wrong scope */this.isAccessible() && this.allowedToEnter.redHouse();
},
shouldAccess__Local: function () {
return this.isAccessible() && /* wrong scope */this.allowedToEnter.redHouse();
},
},
// ...
];
}
let logic = new Logic();
console.log(logic.houses[0].shouldAccess__Global());
// returns: isAccessible is not a function
console.log(logic.houses[0].shouldAccess__Local());
// returns: Cannot read property 'redHouse' of undefined
Defining an arrow function binds the class-scope to this. Using the classic function syntax, it binds this to the scope of it's "owner", the house-object inside the array. I understand that, in both cases, this is correct behavior.
Is there an elegant way to write the shouldAccess method, giving access to both scopes?
A possible solution I came up with is the following:
shouldAccess__withHelper: function () {
return this.isAccessible() && this.helper().allowedToEnter.redHouse();
},
helper: () => {
return this;
},
This is not elegant thought. I have a large amount of house-objects in my array. Giving each object a helper function sucks.
I know that the whole data-structure is a bit convoluted and not ideal. I'm not asking for advice on how to structured my code better.
I'm working with a large amount of logic that's in the given structure and refactoring everything would be a huge pain.
Logic doesn't seem to be a proper entity for a class. While houses seem to be proper entities, but they aren't classes.
In JS OOP, this usually refers to class instance, as a rule of thumb. Рouses classes would make it easier to handle house-logic relationship, and 'logic' instance is provided via dependency injection:
class AbstractHouse {
constructor(logic, name) {
this.name = name;
this.logic = logic;
}
}
class RedHouse extends AbstractHouse {
constructor(logic, name = 'Red House') {
super(logic, name);
}
shouldAccess() {
return this.isAccessible() && this.logic.allowedToEnter.redHouse()
}
}
class Logic {
...
houses = [new RedHouse(this)];
}
The fact that base class should be extended for every house type to list redHouse method name explicitly depending on house type suggests that it's not a perfect relationship and could be improved to be more flexible. It's also not known how many house types are there and whether it's a good idea to have a class for each type.
Even without actual class, houses can follow certain interface to expose 'logic' instance to their methods:
houses = [
{
logic: this, // same as dependency injection in house constructor
name: 'Red House',
shouldAccess() {
return this.isAccessible() && this.logic.allowedToEnter.redHouse()
}
...
}
];
Structuring my thoughts and asking a question helped me come up with another possible solution.
Defining shouldAccess like the following ...
shouldAccess__passLogic: function (logic) {
return this.isAccessible() && logic.allowedToEnter.redHouse();
},
... and passing the class instance to that function on function-call (console.log(logic.houses[0].shouldAccess__Local(logic));) does fix the problem. Not very beautiful, but should at least be easy to refactor.
I'm still open to other suggestions though!

Angular services with default values for non-existing attributes

Working on an Ionic application that performs both in Android and Windows.
There are services, such as Ionic's $ionicLoading, which we override functionality in order to work properly in windows:
angular.factory('$ionicLoading', function(){
return {
show: function (){...} // custom implementation
hide: function (){...} // custom implementation
}
});
But there are other services which we have to override only to not break the app.
In this cases it would be really useful to provide a service that won't do anything. For example:
angular.factory('$ionicExampleService', function(){
return {
*foo*: angular.noop // for operations
*bar*: promise // returns promise
}
});
Note: I know that a better way of doing this would be with a service that chooses between Ionic's implementation or a made one, but this is just for the sake of learning.
The ideal would be going even further, it would be magnificent to be able to return something even more bulletproof. Something like a generic flexible services:
angular.factory('$ionicPopup', function(){
return /*magic*/;
});
$ionicPopup.show({...}) // show was not defined
.then(foo); // won't break and will execute foo()
It is possible?
From what I understood you need to override implementation of existing services. You can do that with an angular service decorator.
A service decorator intercepts the creation of a service, allowing it to override or modify the behaviour of the service. The object returned by the decorator may be the original service, or a new service object which replaces or wraps and delegates to the original service.
For more information you can check angular documentation. One simple example would be:
app.factory('someService', function () {
return {
method1: function () { return '1'; }
method2: function () { return '2'; }
};
});
app.decorator('someService', function ($delegate) {
// NOTE: $delegate is the original service
// override method2
$delegate.method2 = function () { return '^2'; };
// add new method
$delegate.method3 = function () { return '3'; };
return $delegate;
});
// usage
app.controller('SomeController', function(someService) {
console.log(someService.method1());
console.log(someService.method2());
console.log(someService.method3());
});
EDIT: Question - How to override every method in the service?
var dummyMethod = angular.noop;
for(var prop in $delegate) {
if (angular.isFunction($delegate[prop])) {
$delegate[prop] = dummyMethod;
}
}
I hope that this helps you.
Using an evaluation for each assignment based on an object property, similar to this:
myVar = myObj.myPropVar === undefined ? "default replacement" : myObj.myPropVar;
Basically you're using a check for if the property has been defined, substituting a default value if it hasn't, and assigning it if it has.
Alternatively, you can use a modified version of the global function in Sunny's linkback to define defaults for all those properties you might assume to be undefined at specific points in your code.
function getProperty(o, prop) {
if (o[prop] !== undefined) return o[prop];
else if(prop == "foo") return "default value for foo";
else if(prop == "bar") return "default value for bar";
/* etc */
else return "default for missing prop";
}
Hope that helps,
C§
use var a = {}; to declare new variable.

Ember - Custom Computed Property to check if all dependent fields exists

I am creating a form and I am trying to find a simple, elegant way of handling to see if all inputs exist.
Form = Ember.Object.extend({
// section 1
name: null,
age: null,
isABoolean: null,
// section 2
job: null,
numberOfSiblings: null,
isComplete: Ember.computed.and('_isSection1Complete', '_isSection2Complete'),
_isSection1Complete: function() {
var isPresent = Ember.isPresent;
return isPresent(this.get('name')) && isPresent(this.get('age')) && isPresent(this.get('isABoolean'));
}.property('name', 'age', 'isABoolean'),
_isSection2Complete: function() {
var isPresent = Ember.isPresent;
return isPresent(this.get('job')) && isPresent(this.get('numberOfSiblings'));
}.property('job', 'numberOfSiblings')
});
However, this doesn't seem to scale. My actual application will have many sections (over 20 sections).
I am looking into trying to create a re-usable computed property that fits my needs. Take for example the code of what I am going for:
Form = Ember.Object.extend({
// properties...
isComplete: Ember.computed.and('_isSection1Complete', '_isSection2Complete'),
_isSection1Complete: Ember.computed.allPresent('name', 'age', 'isABoolean'),
_isSection2Complete: Ember.computed.allPresent('job', 'numberOfSiblings')
});
I feel that this is a common case, but I'm failing to find the correct computed properties on how to execute this, so I would like to make my own.
Two questions:
Where's the best place to define the custom computed property? Can I just attach a function to Ember.computed?
Is there an easier way to solve this? I feel like I'm overlooking something simple.
As for Question #1,
You can define a custom computed helper in the App namespace. In this example, I created a new computed helper called allPresent that checks each property passed in against Ember.isPresent.
App.computed = {
allPresent: function (propertyNames) {
// copy the array
var computedArgs = propertyNames.slice(0);
computedArgs.push(function () {
return propertyNames.map(function (propertyName) {
// get the value for each property name
return this.get(propertyName);
}, this).every(Ember.isPresent);
});
return Ember.computed.apply(Ember.computed, computedArgs);
}
};
It can be used like this, per your example code:
_isSection2Complete: App.computed.allPresent(['job', 'numberOfSiblings'])
I adapted this from the approach here: http://robots.thoughtbot.com/custom-ember-computed-properties
As for Question #2, I can't think of a simpler solution.
I had to make a minor adjustment to Evan's solution, but this works perfectly for anyone else that needs it:
App.computed = {
allPresent: function () {
var propertyNames = Array.prototype.slice.call(arguments, 0);
var computedArgs = propertyNames.slice(0); // copy the array
computedArgs.push(function () {
return propertyNames.map(function (propertyName) {
// get the value for each property name
return this.get(propertyName);
}, this).every(Ember.isPresent);
});
return Ember.computed.apply(Ember.computed, computedArgs);
}
};
This can now be used as such:
_isSection2Complete: App.computed.allPresent('job', 'numberOfSiblings')

Object oriented approach with AngularJS

It seems that Angular does not provide a built-in solution to define class instances with properties and methods and that it's up the developer to build this.
What is the best practice to do this in your opinion?
How to you link this with the backend?
Some of the tips I have gathered use factory services and named functions.
Sources :
Tuto 1
Tuto 2
Thanks for your insights
I think that the closest structure to an Object it's probably a factory, for several reasons:
Basic Syntax:
.factory('myFactory', function (anInjectable) {
// This can be seen as a private function, since cannot
// be accessed from outside of the factory
var privateFunction = function (data) {
// do something
return data
}
// Here you can have some logic that will be run when
// you instantiate the factory
var somethingUseful = anInjectable.get()
var newThing = privateFunction(somethingUseful)
// Here starts your public APIs (public methods)
return {
iAmTrue: function () {
return true
},
iAmFalse: function () {
return false
},
iAmConfused: function () {
return null
}
}
})
And then you can use it like a standard Object:
var obj = new myFactory()
// This will of course print 'true'
console.log( obj.iAmTrue() )
Hope this helps, I perfectly know that the first impact with angular modules can be pretty intense...
You would use an angular service.
All angular services are singletons and can be injected into any controller.
Ideally you would keep only binding/actions on html in your controller and the rest of the logic would be in your service.
Hope this helps.
I got idea by evaluating this library : https://github.com/FacultyCreative/ngActiveResource
However this library assumes strict rest so I it wasn't work for me. What did work for is this:
I created base Model
var app = angular.module('app', []);
app .factory('Model', function(){
var _cache = {}; // holding existing instances
function Model() {
var _primaryKey = 'ID',
_this = this;
_this.new = function(data) {
// Here is factory for creating instances or
// extending existing ones with data provided
}
}
return Model;
});
Than I took simple function extensions "inherits"
Function.prototype.inherits = function (base) {
var _constructor;
_constructor = this;
return _constructor = base.apply(_constructor);
};
and now I cam creating my models like this
app.factory('Blog', [
'Model',
'$http',
function(Model, $http) {
function Blog() {
// my custom properties and computations goes here
Object.defineProperty(this, 'MyComputed' , {
get: function() { return this.Prop1 + this.Prop2 }
});
}
// Set blog to inherits model
Blog.inherits(Model);
// My crud operations
Blog.get = function(id) {
return $http.get('/some/url', {params: {id:id}}).then(function(response) {
return Blog.new(response.data);
});
}
return Blog;
}
]);
Finally, using it in controller
app.controller('MyCtrl', [
'$scope', 'Blog',
function($scope, Blog) {
Blog.get(...).then(function(blog) {
$scope.blog = blog;
});
}
])
Now, there is much more in our Model and extensions but this would be a main principle. I am not claiming this is best approach but I am working pretty big app and it really works great for me.
NOTE: Please note that I typed this code here and could be some errors but main principle is here.
As my question does not really reflect the issue I was facing, I'll just post my approach for the sake of it :
As Domokun put it, rule of thumb is to decouple front and back. But as I am only building a prototype and managing both ends, I would like to keep things in only one place and let the rest of the application use the central information as a service.
What I want to do here is to build a form through ng-repeat containing the model fields and most importantly how to display information in the form (e.g. 'Last name' instead of 'lastname')
So as I started working around with mongoose models here's what I have managed to do :
Firstly, it is possible to pass the mongoose schema of a model from node side to angular side with an app.get request with the following response :
res.send(mongoose.model('resources').schema.paths);
this spitts out an object containing all fields of the 'resources' collection. On top of that I included some additional information in the model like this :
var resourceSchema = new Schema({
_id: { type: Number },
firstname: { type: String, display:'First name' },
lastname: { type: String, display:'Last name' }
});
mongoose.model('resources', resourceSchema);
So basically I can retrieve this symmetrically on angular side and I have all I need to map the fields and display them nicely. It seems I can also describe the validation but I'm not there yet.
Any constructive feedback on this approach (whether it is valid or totally heretic) is appreciated.

What's the best way to override Model.get(attr) in Backbone.js?

I'm using Backbone.js for the first time, and liking it so far. One thing I can't work out at the moment in dynamic attributes of models. For example, say I have a Person model, and I want to get their full name:
var Person = Backbone.Model.extend({
getFullName: function () {
return this.get('firstName') + ' ' + this.get('surname');
}
});
Then I could do person.getFullName(). But I'd like to keep it consistent with the other getters, more like person.get('fullName'). I don't see how to do that without messily overriding Person#get. Or is that my only option?
This is what I've got so far for the overriding option:
var Person = Backbone.Model.extend({
get: function (attr) {
switch (attr) {
case 'fullName':
return this.get('firstName') + ' ' + this.get('surname');
break;
case 'somethingElse':
return this.doSomethingClever();
break;
default:
return Backbone.Model.prototype.get.call(this, attr);
}
}
});
I suppose it's not terrible, but it seems there should be a better way.
Would this be simpler?
var Person = Backbone.Model.extend({
get: function (attr) {
if (typeof this[attr] == 'function')
{
return this[attr]();
}
return Backbone.Model.prototype.get.call(this, attr);
}
});
This way you could also override existing attributes with functions. What do you think?
I would think of attributes as the raw materials used by a model to provide answers to callers that ask the questions. I actually don't like having callers know too much about the internal attribute structure. Its an implementation detail. What if this structure changes?
So my answer would be: don't do it.
Create a method as you've done and hide the implementation details. Its much cleaner code and survives implementation changes.
The actual properties used by Model.get are stored in the attribute property. You could do something like this:
// function to cross-browser add a property to an object
function addProperty(object, label, getter, setter) {
if (object.defineProperty){
object.defineProperty(object, label, {getter: getter, setter: setter})
}
else {
object.__defineGetter__(label, getter)
object.__defineSetter__(label, setter)
}
}
// inside the initializer of your model, add a property to the attribute object
var Person = Backbone.Model.extend({
initialize: function(attr, options) {
var t = this;
...
addProperty(this.attributes, 'fullName',
function() {return t.get('firstName') + ' ' + t.get('surname'),
function(val) {...}
)
}
})
This will allow you to do person.get('fullName') as you requested.
Edit: To be clear, I agree with Bill's answer below. Shouldn't really be dinking around with the internal implementation of backbone.js. Especially since this is incomplete...what about escape() instead of get()? And the setter is more complex, as it does validation, change notification, etc...now I'm sorry I posted this :)

Categories

Resources