We can create Em.Object like this:
var foo = Em.Object.create({
somevar : '123'
});
and then use it:
foo.get('somevar');
but how to create a private property or method in Em.Object which would be accessable from the object but wouldnt be ourside?
There is indeed a way to have private variables in Ember objects, as follows:
MyObject = Ember.Object.extend({
init: function() {
// private variable
var a = 1;
// methods to get, set, or otherwise accesss the private variables
this.getA = function() {return a;};
this.setA = function(val) {a = val;}
// don't forget this!
this._super(...arguments);
}
});
now try
o1 = MyObject.create()
o2 = MyObject.create()
o1.setA(42);
o2.getA(); //1
In other words, you have to declare the private variables, AND any getters, setters, or other routines that want to use them, in the init hook. Of course, this means that those getters/setters will be present on each instance of the class, rather than in its prototype. That's slightly inefficient, but the same holds for any approach to private variables for classes in JavaScript.
It is conceivable that Ember could introduce a new private: {} hash on objects, but then Ember would need a lot of machinery to handle finding and controlling access to private variables across class hierarchies. It would be equivalent to redesigning or extending the language itself, which is not part of the Ember mission.
Meanwhile, the above approach works fine if the number of private instance variables is limited and the number of routines that need to access them is small. So the accepted answer that says this is impossible is, well, wrong.
It's not possible, since Ember.js does not provide any encapsulation mechanisms.
However, you can simply use some convention for private members. For example, prefix them with _ sign.
You could use a closure:
(function() {
var somePrivateProperty = 'xyz';
MyObject = Em.Object.extend({
someComputedProperty: function() {
return 'somePrivateProperty = ' + somePrivateProperty;
}).property()
})
})();
Is possible with a little trick:
var obj = Em.Em.Object.create(
new function(){
var privateVar = "this is private";
this.getPrivateVar = function(){
return privateVar ;
}
},
{
emberVar: "Ember var",
emberMethod : function(){
return this.getPrivateVar();
},
emberMethod1 : function(){
return privateVar ;
},
emberBinding : 'emberVar'
}
)
now if U try to get private var
obj.privateVar
> unknown
obj.getPrivateVar()
> "this is private"
obj.emberMethod()
> "this is private"
The only problem is that:
obj.emberMethod1()
> unknown
Related
When I tried to do unit testing for private methods in a Class getting error as private methods are only accessible inside the class. Here I added sample snippet for my class and mocha test. Kindly provide me solution to implement unit test for private methods.
Class Name: Notification.ts
class Notification {
constructor() {}
public validateTempalte() {
return true;
}
private replacePlaceholder() {
return true;
}
}
Unit Test:
import {Notification} from 'Notification';
import * as chai from "chai";
describe("Notification", function(){
describe('#validateTempalte - Validate template', function() {
it('it should return success', function() {
const result = new Notification()
chai.expect(result.validateTempalte()).to.be.equal(true);
});
});
describe('#replacePlaceholder - Replace Placeholder', function() {
it('it should return success', function() {
const result = new Notification()
// As expected getting error "Private is only accessible within class"
chai.expect(result.replacePlaceholder()).to.be.equal(true);
});
});
});
As a workaround, currently, I am changing access specifier of function replacePlaceholder to public. But I don't think its a valid approach.
A possible solution to omit Typescript checks is to access the property dynamically (Not telling wether its good).
myClass['privateProp'] or for methods: myClass['privateMethod']()
Technically, in current versions of TypeScript private methods are only compile-time checked to be private - so you can call them.
class Example {
public publicMethod() {
return 'public';
}
private privateMethod() {
return 'private';
}
}
const example = new Example();
console.log(example.publicMethod()); // 'public'
console.log(example.privateMethod()); // 'private'
I mention this only because you asked how to do it, and that is how you could do it.
Correct Answer
However, that private method must be called by some other method... otherwise it isn't called at all. If you test the behaviour of that other method, you will cover the private method in the context it is used.
If you specifically test private methods, your tests will become tightly coupled to the implementation details (i.e. a good test wouldn't need to be changed if you refactored the implementation).
Disclaimer
If you still test it at the private method level, the compiler might in the future change and make the test fail (i.e. if the compiler made the method "properly" private, or if a future version of ECMAScript added visibility keywords, etc).
In my case, I use the prototype of the object to get access to a private method. It works well and TS does not swear.
For example:
class Example {
private privateMethod() {}
}
describe() {
it('test', () => {
const example = new Example();
const exampleProto = Object.getPrototypeOf(example);
exampleProto.privateMethod();
})
}
If you use a static method then use exampleProto.constructor.privateMethod();.
In HolgerJeromin's comment, the comment issue has a succinct solution that still uses the property syntax.
The solution is to type cast your object / class to any.
Examples:
(<any>myClass).privateMethod();
const value = (<any>myClass).privateValue;
(myClass as any).privateMethod();
const value = (myClass as any).privateValue;
This method satisfies the compiler as well as the VSCode syntax highlighting.
Here are some of my notes from the issue that talks about this
Accessing via a string is more common, although I don't see why it might be more typesafe.
These features are done deliberately, therefore they are helping more than hindering.
There is probably a way to disable this type of feature so people don't copy and paste this code into production. "noImplicitAny": true, might help in the tsconfig.json
Extract out the private function into a separate/stand alone function, but don't export it externally.
This is somewhat semantically correct, since after all — a private function is private and should not be accessed by anyone except the class itself.
My subjective solution: you could define a new testing-only interface that extends the original one by adding the private methods as (implicitly public) interface methods. Then, you cast the instantiated object to this new test type. This satisfies both tsc and VS code type checking. Your example with my solution:
interface INotification {
validateTemplate(): boolean,
}
class Notification implements INotification {
constructor() {}
public validateTemplate() {
return true;
}
private replacePlaceholder() {
return true;
}
}
Testing:
import {Notification} from 'Notification';
import * as chai from "chai";
interface INotificationTest extends INotification {
replacePlaceholder(): boolean;
}
describe("Notification", function(){
describe('#validateTemplate - Validate template', function() {
it('it should return success', function() {
const result = new Notification() as INotificationTest;
chai.expect(result.validateTemplate()).to.be.equal(true);
});
});
describe('#replacePlaceholder - Replace Placeholder', function() {
it('it should return success', function() {
const result = new Notification() as INotificationTest;
// Works!
chai.expect(result.replacePlaceholder()).to.be.equal(true);
});
});
});
Advantages:
tsc and vs code do not complain
IntelliSense (or any other autocomplete) works
simple (subjectively)
If you don't want to define the original interface (INotification), you could just fully define the test one (INotificationTest) instead of extending and cast it in the same manner.
Disadvantages:
Added boilerplate
Need to have both of the interfaces updated and in sync
Potentially introducing bugs by explicitly casting as a non original type.
I leave it up to you to decide whether this is worth it or no. In my case, the positives outweigh the negatives. I have tested this with jest, but I assume that mocha.js is no different here.
Edit: but generally I would agree with Fenton's answer
// module.ts
private async privateMethod = () => "private method executed"
public async testPrivateMethods(...args) {
if (process.env.NODE_ENV === 'development') {
return this.privateMethod(...args);
}
}
Now we can reach our private method to test. In jest file:
// module.spec.js
describe('Module', () => {
let service: Module = new Module();
it('private method should be defined', () => {
expect(service.testPrivateMethods).toBeDefined();
});
}
You need to set your enviroment variable name of NODE_ENV must be development.
// .env
NODE_ENV="development"
The fun thing is that it's just a typescript error (not javascript), so you can fix it with
// #ts-expect-error
and everything works fine.
I consider it as a legitimate solution, as the goal was to suppress typescript in this particular case.
Since private methods are not accessible outside class, you can have another public method which calls replacePlaceholder() in Notification class and then test the public method.
I have a TypeScript +2.4 project where I'm using Jest for my unit tests. The project has a lot of poco models, without a default value. For example:
export class Foo {
public id: number
public name: string;
public when: Date;
}
Each of these models is mapped from raw json to this class. It is a requirement for my tests that all properties are assigned, e.g. have values. This leads to the following test that has to be written for all models:
test('Foo() should have its properties assigned', () => {
const target: Foo = {
id: 1001, name: 'whatever', when: new Date()
};
// manually assert each propertie here
expect(target.id).toBeDefined();
expect(target.name).toBeDefined();
expect(target.when).toBeDefined();
}
To me, that's not so DRY to do for each test. Not to mention error prone and cumbersome. What I would like to do is create a helper that iterates through each property and asserts that it has a value assigned.
Example 1 - Object.keys
This example is incorrect because Object.keys only iterates through the already assigned properties, ignoring the non-set properties (and thus always is positive):
public static AssertAllPropertiesAreAssigned(target: object): void {
Object.keys(target).forEach((key, index) => {
expect(target[key]).toBeDefined();
});
Example 2 - Object.getOwnPropertyNames()
The same as example 1:
public static AssertAllPropertiesAreAssigned(target: object): void {
Object.getOwnPropertyNames(target).forEach((name, index) => {
expect(target[name]).toBeDefined();
});
Example 3 - Set default values
By assigning a default value to each poco, like null, I can make the earlier samples work. But I'd sure like to avoid that at all cost:
export class Foo {
public id: number = null;
public name: string = null;
public when: Date = null;
}
The question: is there a way to create a helper that asserts that each property of my TypeScript poco object is actually assigned a value, in my test? Or, as an alternative, does Jest have some util for this?
There are similar questions on SO, but they are not related to asserting the values in a test. This makes this question, as far as I've looked around, differ from the others:
How to dynamically assign value to class property in TypeScript
How to iterate through all properties and its values in Typescript class
Typescript looping trough class type properties
Also, I'm aware that the Javascript compiled output of my poco will probably leads to that the unset properties are simply not available:
var Foo = (function() {
// nothing here...
}());
But with TypeScript's strong typing power and recent changes and/or Jest helpers, there might be some additional options to get this done?
Most of your options aren't any better than the answers to those other questions: initialize the properties (good idea); use property decorators (tedious).
Personally, I think it should be an error to declare a class property as a can't-be-undefined type like string and then not define it in the constructor, but that feature isn't part of TypeScript yet, even if you turn on strictNullChecks (which you should). I don't know why you don't want to initialize the variables, but this would work:
export class Foo {
public id: number | undefined = void 0;
public name: string | undefined = void 0;
public when: Date | undefined = void 0;
}
Now an instance of Foo will have the relevant keys if you do Object.keys() even though the values will still be undefined.
Wait a minute, you're not even using the class at runtime:
const target: Foo = {
id: 1001, name: 'whatever', when: new Date()
}; // object literal, not constructed class instance
console.log(target instanceof Foo) // false
Then I suggest you use an interface instead of a class, and just turn on strictNullChecks:
export interface Foo {
id: number;
name: string;
when: Date;
}
const target: Foo = {
id: 1001, name: 'whatever', when: new Date()
};
const badTarget: Foo = {
id: 1002;
}; // error, Property 'name' is missing
Now TypeScript will not let you assign a possibly-undefined value to those properties and you don't have to bother looping over anything at runtime.
Hope that helps!
Say I have an Ember.Object obj, with a property propPath.
I'm trying to implement:
function isComputedPropertyWithNoSetter(obj, propPath) {
// what do I do here?
// something involving Ember.meta(obj) perhaps?
}
So I can do:
var hasStaticProp = Ember.Object.extend({ prop: 5 }).create();
isComputedPropertyWithNoSetter(hasStaticProp, 'prop');
// => false
var hasComputedPropertyWithSetter = Ember.Object.extend({ prop: function (k, v, d) { }.property() }).create();
isComputedPropertyWithNoSetter(hasComputedPropertyWithSetter, 'prop');
// => false
var hasComputedPropertyNoSetter = Ember.Object.extend({ prop: function () { }.property() }).create();
isComputedPropertyWithNoSetter(hasComputedPropertyNoSetter, 'prop');
// => true
I'm writing 'tree-walking' state serialization code for a large established ember codebase. When I restore state, I want a guard check to make sure I never accidentally overwrite a read-only (getter only) computed property with a static value.
I need to implement this function so I can do....
if (!isComputedPropertyWithNoSetter(obj, propPath) {
// not going to accidentally overwrite a computed property with a static value
Ember.set(obj, propPath, serializedStaticValue);
}
I realize this is fairly dicey, and the solution might be a not entirely recommended hack.
Consider this situation. I have a common logic which I want to reuse across Ember.ArrayController and Ember.ObjectController instances.
Both Ember.ArrayController and Ember.ObjectController are derived from a basic Ember.Object so I am trying something like:
AbstractController = Ember.Object.extend({
// some methods with common logic
});
AbstractArrayController = AbstractController.extend({});
AbstractObjectController = AbstractController.extend({});
The problem is I also need AbstractArrayController and AbstractObjectController to extend from their parents (Ember.ArrayController and Ember.ObjectController).
How would I achieve this sort of inheritance?
I am looking at reopen and reopenClass methods right now, maybe they could be useful: http://emberjs.com/api/classes/Ember.Object.html#method_reopen
I tried just doing something like:
Ember.Object.reopenClass({
foo: function () {
return "foo";
}.property("foo")
});
But that doesn't seem to work.
Another way to put the problem:
App.HelloController = Ember.ObjectController.extend({
foo: function () {
return "foo";
}.property("foo")
});
App.WorldController = Ember.ObjectController.extend({
foo: function () {
return "foo";
}.property("foo")
});
How to abstract the foo computed property?
reopenClass adds methods on the class object, not the instance objects. When you do:
Ember.Object.reopenClass({
foo: function () {
return "foo";
}.property("foo")
});
You are creating Ember.Object.foo().
You need to use reopen if you want to methods at an instance level, for example Ember.Object.create().foo().
To answer you question, the best way to abstract a function that many types of objects can use is with a mixin. To create a mixin you use.
var mixin = Ember.Mixin.create({
foo: function() {
return 'foo';
}
});
And to have your objects take advantage of that mixin you can use.
var MyController = Ember.ObjectController.extend(mixin, {
// ...
});
More about mixins: http://codingvalue.com/blog/emberjs-mixins/ and http://emberjs.com/api/classes/Ember.Mixin.html
Below I have provided an example of what I 'think' should work, but does not. My expectation is that if a controller inherits from another controller, than it should be able to access provided behaviors in the parent class. I know that the same behavior can be achieved with 'needs', but I think it would be much cleaner if you could inherit behavior.
https://gist.github.com/4589210
You can alternatively use mixin. What it is? It is kind of multi inheritance.
App.Important = Ember.Mixin.create({
sharedBehavior: function() {
return "A winner is you!!!";
}.property()
});
App.OtherImportant = Ember.Mixin.create({
otherSharedBehavior: function() {
return "A winner is not you!!!";
}.property()
});
App.AnotherController = Ember.controller.extend(App.Important, App.OtherImportant, {
importantStuff: function() {
var ohBeehave = this.get("sharedBehavior");
if(ohBeehave) {
return ohBeehave;
} else {
return "FML";
}
}.property("sharedBehavior")
});
See http://emberjs.com/api/classes/Ember.Mixin.html
You are correct that controllers should be able to inherit properties from other controllers. Here is a working example based on your gist:
http://jsbin.com/uzeyum/1/edit
App = Ember.Application.create();
App.ApplicationController = Ember.Controller.extend({
sharedBehavior: function() {
return "A winner is you!!!";
}.property()
});
App.AnotherController = App.ApplicationController.extend({
importantStuff: function() {
var ohBeehave = this.get("sharedBehavior");
if(ohBeehave) {
return ohBeehave;
} else {
return "FML";
}
}.property("sharedBehavior")
});
That said, from experience I can tell you that inheritance is very rarely what you want. There are many reasons for this, see Prefer composition over inheritance? for detail.
By declaring an array of dependencies via the needs property rather than inheritance you will find over time that your application is less brittle and easier to test/change.