What is the best way of creating an object of Singletons? I have a class which will be shared between different applications. This class should be a singleton PER application.
Here is what I currently have. However, when I instantiate app1 twice, it creates a new instance for that.
class Sample {
constructor(appName) { // eslint-disable-line
if (!Sample._instance[appName]) {
Sample._instance[appName] = this
console.log('Creating new instance')
this.counter = 0
}
return Sample._instance[appName]
}
getVal () {
this.counter++
console.log('counter: ' + this.counter)
}
}
Then I call it like this:
import Sample from './sample'
const sample1 = new Sample('app1')
sample1.getVal() // OK - prints 1
sample1.getVal() // OK - prints 2
const sample1a = new Sample('app1')
sample1a.getVal() // NOK - prints 1 - should print 3
const sample2 = new Sample('app2')
sample2.getVal() // OK - prints 1
sample2.getVal() // OK - prints 2
If instead I do something like below, then how can I actually pass in appName when the instance is created already during import?
const sample = new Sample(appName)
export default sample
Just adding static _instance = {} got rid of the runtime error I encountered and made it work as you want.
I also tested this in nodejs to make sure there's not anything weird going on when importing the class instead of declaring it in the same file.
class Sample {
static _instance = {};
constructor(appName) { // eslint-disable-line
if (!Sample._instance[appName]) {
Sample._instance[appName] = this
console.log('Creating new instance')
this.counter = 0
}
return Sample._instance[appName]
}
getVal () {
this.counter++
console.log('counter: ' + this.counter)
}
}
const sample1 = new Sample('app1')
sample1.getVal() // OK - prints 1
sample1.getVal() // OK - prints 2
const sample1a = new Sample('app1')
sample1a.getVal() // NOK - prints 1 - should print 3
const sample2 = new Sample('app2')
sample2.getVal() // OK - prints 1
sample2.getVal() // OK - prints 2
Differentiate the roles of the container object and the singleton.
Below I made an IIFE singleton container that handles creating and storing new instances of Sample and it returns an existing instance if it's already stored.
You can keep the class definitions and container singleton in their own file and export the container. Since it's already invoked in the module, you just need to call container.get('app') in the importing scripts.
class Sample {
constructor() {
console.log('Creating new instance')
this.counter = 0
}
getVal () {
this.counter++
console.log('counter: ' + this.counter)
}
}
const container = (() => {
const singletons = {};
const get = (appName) =>{
if (!singletons[appName]){
singletons[appName] = new Sample();
}
return singletons[appName]
}
return { get }
})();
let sample1 = container.get('app1')
sample1.getVal();
sample1.getVal();
let sample1A = container.get('app1')
sample1A.getVal();
let sample3 = container.get('app2')
sample3.getVal();
Related
I will try to be brief and to the point. In our school we use Google Sheets as a class book. I have a script that counts the absences of individual students. The script is written in Google Apps Scripts (GAS), but the purpose of the application and even GAS are irrelevant to this question.
I'm currently trying to rewrite my old code and try to implement Jest unit testing. So my question will be simple. How can I mock a global variable that I call inside the function under test?
Here I paste the part of the code that introduces the global variables:
const absenceFileId = "some_string"
const teachersFileId = "some_string"
const studentGroupsId = "some_string"
const classbookFolderId = "some_string"
// STATE
let teachers = {}
let groups = {}
let subjectCounter = {}
let errors = []
let students = {}
function setupStateVariables() {
teachers = getTeachers(removeEmptyRowsAndHeader(getValuesArray(teachersFileId, 0, 1, 1, 50, 3)));
groups = getGroups(removeEmptyRowsAndHeader(getValuesArray(studentGroupsId, 0, 1, 1, 400, 6)))
subjectCounter = createSubjectCounter(removeEmptyRowsAndHeader(getValuesArray(studentGroupsId, 1, 1, 1, 200, 4)))
}
Subsequently, I need to update the global variable subjectCounter inside the function:
function updateSubjectCounter(group, schoolClass, subject) {
if (!(subject in subjectCounter[schoolClass])) {
return
}
if (group) {
subjectCounter[schoolClass][subject][group] += 1
} else {
let keys = Object.keys(subjectCounter[schoolClass][subject])
if (keys.length > 1) {
for (let key of keys) {
subjectCounter[schoolClass][subject][key] += 1
}
} else {
subjectCounter[schoolClass][subject][null] += 1
}
}
}
I have the previous code in the main.js module.
I export the function for testing as follows. If statement is here due to GAS enviroment.:
if (typeof module !== 'undefined') {
module.exports = {
"getTeachers": getTeachers,
"getGroups": getGroups,
"createSubjectCounter": createSubjectCounter,
"updateSubjectCounter": updateSubjectCounter
}
}
For testing, I use the main.test.js module, which looks like this:
const main = require('./main');
test("Test funkce updateSubjectCounter", () => {
main.updateSubjectCounter("", "a", "IFM")
expect(subjectCounter).toEqual({something})
The question is how to mock the global variable subjectCounter in this test?
EDIT: I am using local enviroment with NodeJS for testing.
I'm trying to create the same behavior of PHP __callStatic magic method in Node.js.
I'm trying to use Proxy to do that, but I don't really know if it's the best option.
class Test {
constructor() {
this.num = 0
}
set(num) {
this.num = this.num + num
return this
}
get() {
return this.num
}
}
const TestFacade = new Proxy({}, {
get: (_, key) => {
const test = new Test()
return test[key]
}
})
// Execution method chain ends in get
console.log(TestFacade.set(10).set(20).get())
// expected: 30
// returns: 0
// Start a new execution method chain and should instantiate Test class again in the first set
console.log(TestFacade.set(20).set(20).get())
// expected: 40
// returns: 0
The problem is that the get trap is fired every time that I try to access a property of TestFacade. The behavior that I need is that when the set method is called it will return this of Test class and I can even save the instance for latter usage!
const testInstance = TestFacade.set(10) // set method return this of `Test` not the Proxy
If something isn't clear, please let me know.
I don't know if it's the best option. But I solved it, returning a new Proxy inside get trap that uses the apply trap to bind the test class instance into the method:
class Facade {
static #facadeAccessor
static createFacadeFor(provider) {
this.#facadeAccessor = provider
return new Proxy(this, { get: this.__callStatic.bind(this) })
}
static __callStatic(facade, key) {
/**
* Access methods from the Facade class instead of
* the provider.
*/
if (facade[key]) {
return facade[key]
}
const provider = new this.#facadeAccessor()
const apply = (method, _this, args) => method.bind(provider)(...args)
if (provider[key] === undefined) {
return undefined
}
/**
* Access the properties of the class.
*/
if (typeof provider[key] !== 'function') {
return provider[key]
}
return new Proxy(provider[key], { apply })
}
}
class Test {
num = 0
set(num) {
this.num = this.num + num
return this
}
get() {
return this.num
}
}
const TestFacade = Facade.createFacadeFor(Test)
console.log(TestFacade.set(10).set(20).get()) // 30
console.log(TestFacade.set(5).set(5).get()) // 10
const testInstance = TestFacade.set(10)
console.log(testInstance.num) // 10
console.log(testInstance.get()) // 10
console.log(testInstance.set(10).get()) // 20
I am coming from vue and used to composable functions. I am trying to figure out the way to do this in svelte
So I make a js file and import store and then was trying to make a function that I could call on multiple components and act individually
swipe.js file
import { writable, derived, get } from 'svelte/store';
function createSwipe() {
const dyFromStart = writable(0)
function moveEvent(eventType, val){
console.log('moveEvent', eventType, val, get(dyFromStart))
dyFromStart.update(n => n + 1);
}
const dxScore = derived(dyFromStart, $dyFromStart => $dyFromStart + 3)
const dyScore = derived(dyFromStart, $dyFromStart => Math.round($dyFromStart + 100));
return {
moveEvent,
dxScore,
dyScore,
};
}
export const swipe = createSwipe();
then in .svelte component import function in script and decompose into subparts
<script>
import { swipe } from "$lib/swipe";
let { moveEvent, dxScore, dyScore } = swipe
</script>
<p>{$dxScore}{$dyScore}</p>
<button on:click="() => moveEvent">button</button>
Well eventually I want to turn into a swipe component hence name but trying to get fundamentals down. So I want to be able to have unique store for each component and for this if I use multiple of this .svelte component the state is shared amongst all.
And not just like three idk modal.svelte components I want to use swipe for a bunch of diff components maybe a photoViewer.svelte right just generic swipe function and use same code for all.
or would I just have to keep the state like const dyFromStart = writable(0) be just let dyFromStart = 0 in each .svelte component and pass it into a pure js function that returns results and update local .svelte variables
Adding this as the non store more pure js things I was trying but couldn't get to be reactive so accepting the answer below on store method that worked and sounds like is the correct approach
export function createSwipe() {
let dyFromStart = 0
function moveEvent(eventType, val){
console.log('moveEvent', eventType, val, dyFromStart, dxScore(), dyScore())
dyFromStart++
}
function dxScore(){ return dyFromStart + 3 }
// const dzScore = derived(dyFromStart, $dyFromStart => $dyFromStart + 3)
const dyScore = () => Math.round(dyFromStart + 100)
return {
moveEvent,
dxScore,
dyScore,
dyFromStart
};
export function createSwipe() {
let dyFromStart = 0
let dxScore = dyFromStart + 3
let dyScore = Math.round(dyFromStart + 100)
function moveEvent(eventType, val){
console.log('moveEvent', eventType, val, dyFromStart, dxScore, dyScore)
dyFromStart++
dxScore = dyFromStart + 3
dyScore = Math.round(dyFromStart + 100)
}
return {
moveEvent,
dxScore,
dyScore,
dyFromStart
};
I suppose that works fine just not reactive with $ and need to call to update a diff local var if doing that
this would seem most sveltey to me or something like it as far as composable function type style not store type
export function createSwipe() {
let dyFromStart = 0
function moveEvent(eventType, val){
console.log('moveEvent', eventType, val)
dyFromStart++
}
$: dxScore = dyFromStart + 3
$: dyScore = Math.round($dyFromStart + 100)
return {
moveEvent,
dxScore,
dyScore,
};
}
I don't understand the question fully, so I try to reiterate first what I think you want:
You want to use your swipe function in multiple places
Each usage of that swipe function should be independent of all others
If that's correct, then the answer is simple: Don't do export const swipe = createSwipe(). Delete that part and instead export the create function to use directly within your components. That way you create a new independent instance each time:
<script>
import { createSwipe } from "$lib/swipe";
let { moveEvent, dxScore, dyScore } = createSwipe()
</script>
<p>{$dxScore}{$dyScore}</p>
<button on:click="() => moveEvent">button</button>
the code in the example works fine but in my codes it doesn't
I`m trying to update object with a new property
const overrides = {paths:{"/":{}}}
const aItems = [{endpoint:"/test"}]
const mockOverrides = JSON.parse(JSON.stringify(overrides));
aItems.forEach(({endpoint}) => {
if (!mockOverrides.paths[endpoint]) {
mockOverrides.paths[endpoint] = {};
}
console.log(mockOverrides); // result {paths:{"/":{}}} expected {paths:{"/":{}, "/test":{}}}
console.log(mockOverrides.paths[endpoint]) // result is {} twice
})
as you can see the property is not displayed in the output
but is somehow exist why this happening?
After adding a ) to the end of the foreach method, it appears to be working fine:
const overrides = {paths:{"/":{}}}
const aItems = [{endpoint:"/test"}]
const mockOverrides = JSON.parse(JSON.stringify(overrides));
aItems.forEach(({endpoint}) => {
if (!mockOverrides.paths[endpoint]) {
mockOverrides.paths[endpoint] = {};
}
console.log(mockOverrides); // result {paths:{"/":{}}} expected {paths:{"/":{}, "/test":{}}}
console.log(mockOverrides.paths[endpoint]) // result is {} twice
});
Yeah. I wrote that but it was deleted. You were missing the ). Other than that the code is fine.
Maybe because the new property was set to __proto__ of mockOverrides.paths
why console.log doesn't show values of properties added by PROTOTYPES in javascript when whole object is printed?
You can reproduce it by run below code on chrome console (also get from the quote above)
var Person=function (name) {
this.Fname=name;
this.health=100;
};
var Mateen=new Person("Mateen");
console.log(Mateen);
// result: { Fname: 'Mateen', health: 100 }
Person.prototype.level=1;
console.log(Mateen);
// result: { Fname: 'Mateen', health: 100 }
console.log(Mateen.level);
// result: 1
Okay, I realize this can be considered subjective, but I'm trying to better understand how to consider scope when writing modules that only expose what's needed publicly. I have a string utility that I've written as an object literal below:
const substrings = {
query: {},
text: "",
results: [],
exists: function (index) {
const exists = index >= 0
return exists
},
check: function () {
const q = this.query
const start = q.openIndex
const stop = q.closeIndex
if (q.hasOpen && !q.hasClose) {
console.log("Missing closing delimiter.")
}
if (!q.hasOpen && q.hasClose) {
console.log("Missing opening delimiter.")
}
if (q.hasOpen && q.hasClose && start > stop) {
console.log("Closing delimiter found before opening.")
}
if (!q.hasOpen && !q.hasClose && this.results.length == 0) {
console.log("No results found.")
}
const order = start < stop
const check = q.hasOpen && q.hasClose && order
return check
},
update: function () {
const q = this.query
const text = this.text
q.before = this.text.indexOf(q.open)
q.start = q.before + q.open.length
this.text = text.slice(q.start, text.length)
q.stop = this.text.indexOf(q.close)
q.after = q.stop + q.close.length
q.openIndex = q.before
q.closeIndex = q.before + q.stop
q.hasOpen = this.exists(q.openIndex)
q.hasClose = this.exists(q.stop)
const newPosition = q.start + q.after
q.position = q.position + newPosition
this.query = q
},
substrings: function () {
const q = this.query
const current = this.text.slice(0, q.stop)
const fullLength = this.text.length
this.text = this.text.slice(q.after, fullLength)
this.results.push(current)
this.update()
if (this.check()) {
this.substrings()
}
},
init: function (open, close, text) {
this.results = []
this.query = {
open,
close,
position: 0,
}
this.text = text
this.update()
},
getSubstrings: function (open, close, text) {
this.init(open, close, text)
if (this.check()) {
this.substrings()
return this.results
}
},
getSubstring: function (open, close, text) {
this.init(open, close, text)
if (this.check()) {
return this.text.slice(0, this.query.stop)
}
}
}
I want to use it as a Node module and expose the getSubstring and getSubstrings methods, but if I were to do:
module.exports = {
all: substrings.getSubstrings,
one: substrings.getSubstring
}
I would get an error due to the usage of this. I realize that if I replace this with the object var name substrings to reference it directly, it works. I could also refactor it to be one big function or smaller functions and just export the 2 I need.
I am trying to go about learning things the right way and am struggling with how I should be thinking about context. I understand how this changes here, but I feel like I'm not fully wrapping my head around how I should consider context when structuring my code.
Is there a more elegant solution to expose methods with code like this that wasn't written to separate private and public methods?
A simple solution would be to bind the exported functions to the proper calling context inside the exports object:
module.exports = {
all: substrings.getSubstrings.bind(substrings),
one: substrings.getSubstring.bind(substrings)
}
Personally, I prefer using the revealing module pattern over object literals for situations like this. With the revealing module pattern, create an IIFE that returns the desired functions, referring to local variables instead of properties on this. For example:
const { getSubstrings, getSubstring } = (() => {
let query = {}
let text = ''
let results = []
function exists(index) {
return index >= 0
}
function check() {
const q = query;
// ...
}
...
function getSubstrings(open, close, text) {
}
...
return { getSubstrings, getSubstring };
})();
module.exports = {
all: getSubstrings,
one: getSubstring
}
This is somewhat opinion-based, but code can be easier to read when there aren't any this references to worry about.