Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Chas McGill
Oct 29, 2010

loves Fat Philippe
Anyone have a recommendation for a graphql course or training resource for someone with a lot of frontend JS experience, not so much backend?

Adbot
ADBOT LOVES YOU

sterster
Jun 19, 2006
nothing
Fun Shoe
Just found this thread and was wondering how you'd deal with this issue. When I used to do java you can make overloaded methods that accept different params. With JS I wasn't sure how to best handle this. What's your thoughts?

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

sterster posted:

Just found this thread and was wondering how you'd deal with this issue. When I used to do java you can make overloaded methods that accept different params. With JS I wasn't sure how to best handle this. What's your thoughts?
There's no overloads in JS or TS, you can either do fooInt, fooString, fooSomeObject, or (not recommended) you can do a function foo that takes a dynamic type, run a switch on the type, and call the appropriate fooInt/fooString/fooSomeObject from the switch.

Or if you just want overloads that accept fewer or more params, in JS params are optional so you can just check if each one is undefined and do the appropriate branching in the function.

sterster
Jun 19, 2006
nothing
Fun Shoe

roomforthetuna posted:

There's no overloads in JS or TS, you can either do fooInt, fooString, fooSomeObject, or (not recommended) you can do a function foo that takes a dynamic type, run a switch on the type, and call the appropriate fooInt/fooString/fooSomeObject from the switch.

Or if you just want overloads that accept fewer or more params, in JS params are optional so you can just check if each one is undefined and do the appropriate branching in the function.

Okay, the checking undefined is something we thought about. We ended up just naming them. makeRequestId(id) and makeRequestGuid(guid) that make a request to an underlying service that accepts both. It's not the worst but was curious if there was a more elegant way I could have done it. Thanks

go play outside Skyler
Nov 7, 2005


Another way to do it would be with a parameter-object but I guess in your case you couldn't prevent someone from calling your function with both a GUID and an ID.

Roadie
Jun 30, 2013

go play outside Skyler posted:

Another way to do it would be with a parameter-object but I guess in your case you couldn't prevent someone from calling your function with both a GUID and an ID.

If you use Typescript you can use
code:
function makeRequest(options: {guid: string} | {id: number})
to enforce code sanity, then have safe default behavior of prioritizing one or the other in case something weird happens anyway.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Roadie posted:

If you use Typescript you can use
code:
function makeRequest(options: {guid: string} | {id: number})
to enforce code sanity, then have safe default behavior of prioritizing one or the other in case something weird happens anyway.
I nearly said the option of an object parameter or weird typescript things, but really you'll be better off with the one function per parameter type, and if they have common code then have all the variant functions call into one common function after you've mapped the parameter to whatever it ends up as.

It'll be both the most performant and easiest to read and understand option.

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:
Hopefully the right place to ask this:

I did some digging into an online cut optimizer out of curiosity, and I pulled this bit of code out of it. Is it JSON or something I haven't learned about Javascript yet?

code:
var CutList = (function() {
	function maxSize() {
		var arr = s.stock_array;
		var high = 0;
		for(var i = 0, len = arr.length; i < len; i++){
			high = (high < arr[i].size) ? arr[i].size : high;
		}
		return high;
	}
	var s, cl = {
		settings: {
			cut_array:      [],
			stock_array:    [],
			cut_list:       [],
			stock_list:     [],
			kerf:           1.5,
			stock:          8000,
			cut_ul:		$('#cut-list'),
			stock_ul:	$('#stock-list'),
			cut_length:	$('#cut-form input[name*="length"]'),
			cut_quantity:	$('#cut-form input[name*="quantity"]'),
			stock_length:	$('#stock-form input[name*="length"]'),
			stock_quantity:	$('#stock-form input[name*="quantity"]'),
			name_input:	$('#settings-form input[name*="cutlist-name"]'),
			stock_input:	$('#settings-form input[name*="stock-length"]'),
			kerf_input:	$('#settings-form input[name*="kerf"]')
		},
		init: function() {
			s = this.settings;
			this.bindUI();
			this.renderUI();
		},

go play outside Skyler
Nov 7, 2005


D34THROW posted:

Hopefully the right place to ask this:

I did some digging into an online cut optimizer out of curiosity, and I pulled this bit of code out of it. Is it JSON or something I haven't learned about Javascript yet?

code:
var CutList = (function() {
	function maxSize() {
		var arr = s.stock_array;
		var high = 0;
		for(var i = 0, len = arr.length; i < len; i++){
			high = (high < arr[i].size) ? arr[i].size : high;
		}
		return high;
	}
	var s, cl = {
		settings: {
			cut_array:      [],
			stock_array:    [],
			cut_list:       [],
			stock_list:     [],
			kerf:           1.5,
			stock:          8000,
			cut_ul:		$('#cut-list'),
			stock_ul:	$('#stock-list'),
			cut_length:	$('#cut-form input[name*="length"]'),
			cut_quantity:	$('#cut-form input[name*="quantity"]'),
			stock_length:	$('#stock-form input[name*="length"]'),
			stock_quantity:	$('#stock-form input[name*="quantity"]'),
			name_input:	$('#settings-form input[name*="cutlist-name"]'),
			stock_input:	$('#settings-form input[name*="stock-length"]'),
			kerf_input:	$('#settings-form input[name*="kerf"]')
		},
		init: function() {
			s = this.settings;
			this.bindUI();
			this.renderUI();
		},

It's plain old javascript with some jQuery in it.

These
code:
$('#settings-form input[name*="kerf"]')
Are called selectors. It means 'Give me a DOM element which matched: an HTML input with a name containing "kerf" inside an HTML element with an ID of "settings-form"'. These selectors are roughly the same as the ones used in CSS.

jQuery creates a function called $ in the global scope to make it easier to type.

e: nowadays, people are using jQuery less and less, and you can pretty much replace this function with document.querySelectorAll("..."). However, do note that the jQuery $ function returns a jQuery object so you can chain calls. document.querySelectorAll does not.

cutList is probably a self-calling anonymous function. Hard to say without the last few lines of it. Usually self-calling anonymous functions are used to not pollute the global scope with functions that don't need to be used elsewhere. The syntax is like this

code:
(function () {
function thisFunctionWillNotBeAvailableInTheGlobalScope(){
// stuff
}
})()

go play outside Skyler fucked around with this message at 19:17 on Mar 23, 2020

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:
I didn't realize this rabbit hole went so deep :v: I'm barely scratching the surface here, it took me an hour yesterday to figure out why Firefox was throwing an error on .valueOf().



code:
settings: {
			cut_array:      [],
			...
			kerf_input:	$('#settings-form input[name*="kerf"]')
		},
Direction change: I come from a VBA-as-a-hobby background, so I'm interpreting settings:{...} here as a collection, where cut_array...kerf_input are key/value pairs - Javascript objects, based on what W3Schools says, and JSON basically bakes these into a file so it can be more easily worked with. I'd say a class object, but there's no methods to a JS object.

go play outside Skyler
Nov 7, 2005


D34THROW posted:

I didn't realize this rabbit hole went so deep :v: I'm barely scratching the surface here, it took me an hour yesterday to figure out why Firefox was throwing an error on .valueOf().



code:
settings: {
			cut_array:      [],
			...
			kerf_input:	$('#settings-form input[name*="kerf"]')
		},
Direction change: I come from a VBA-as-a-hobby background, so I'm interpreting settings:{...} here as a collection, where cut_array...kerf_input are key/value pairs - Javascript objects, based on what W3Schools says, and JSON basically bakes these into a file so it can be more easily worked with. I'd say a class object, but there's no methods to a JS object.

This is indeed an associative array, also called just an "object". In JavaScript, you can declare new, complex tree objects using a JSON-like notation (it's not exactly the same but close enough for what you care about right now).

An object in JavaScript can have functions, other objects. It's an associative array of whatever you want.

Not exactly sure about my terminology here, maybe someone can chime in. I've been doing JS for so long that I'm basically not thinking about these things anymore.

e: something I make EVERYONE who works in my team read is java script: The Good Parts. The book is relatively short and you can get it as an e-book for a fistful of dollars. Definitely read it if you want to avoid common pitfalls and write good code. Nowadays it's a bit dated in the solutions it offers, what with the new EcmaScript standards, but the gist of it is still very good.

go play outside Skyler fucked around with this message at 19:46 on Mar 23, 2020

putin is a cunt
Apr 5, 2007

BOY DO I SURE ENJOY TRASH. THERE'S NOTHING MORE I LOVE THAN TO SIT DOWN IN FRONT OF THE BIG SCREEN AND EAT A BIIIIG STEAMY BOWL OF SHIT. WARNER BROS CAN COME OVER TO MY HOUSE AND ASSFUCK MY MOM WHILE I WATCH AND I WOULD CERTIFY IT FRESH, NO QUESTION

D34THROW posted:

I didn't realize this rabbit hole went so deep :v: I'm barely scratching the surface here, it took me an hour yesterday to figure out why Firefox was throwing an error on .valueOf().



code:
settings: {
			cut_array:      [],
			...
			kerf_input:	$('#settings-form input[name*="kerf"]')
		},
Direction change: I come from a VBA-as-a-hobby background, so I'm interpreting settings:{...} here as a collection, where cut_array...kerf_input are key/value pairs - Javascript objects, based on what W3Schools says, and JSON basically bakes these into a file so it can be more easily worked with. I'd say a class object, but there's no methods to a JS object.

JS objects can definitely have something analogous to methods - that is, instance members that are functions.

LifeLynx
Feb 27, 2001

Dang so this is like looking over his shoulder in real-time
Grimey Drawer
I was thinking about taking an online React course because I kind of fell out of it after some early experiments, and I have a lot of free time now. I started just as Hooks were coming into favor. Are there any courses that are recommended? Udemy is having another of its sales. There are others like Wes Bos or Tyler McGinnis, but they're more expensive.

Ola
Jul 19, 2004

LifeLynx posted:

I was thinking about taking an online React course because I kind of fell out of it after some early experiments, and I have a lot of free time now. I started just as Hooks were coming into favor. Are there any courses that are recommended? Udemy is having another of its sales. There are others like Wes Bos or Tyler McGinnis, but they're more expensive.

I just followed this video, with lots of pausing, typing, googling and additional reading.

https://youtu.be/Ke90Tje7VS0

Then do Redux afterwards. Then throw it all away and do Elm. :v:

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense
Learn redux and all about why you need to have immutable state and all that. Then try out vuex and realise that having mutable state that just triggers updates when you change it is so... so... so much darn easier.

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice

Nolgthorn posted:

Learn redux and all about why you need to have immutable state and all that. Then try out vuex and realise that having mutable state that just triggers updates when you change it is so... so... so much darn easier.

Until it isn't.

Newf
Feb 14, 2006
I appreciate hacky sack on a much deeper level than you.
Is Array.filter stable? Can I count on elements in the produced array to be in the same order as in the original array? MDN doesn't say anything about it. Limited tests suggest that it is, but I'd like a little more confidence than that...

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Newf posted:

Is Array.filter stable? Can I count on elements in the produced array to be in the same order as in the original array? MDN doesn't say anything about it. Limited tests suggest that it is, but I'd like a little more confidence than that...

Yes.

Read through the ecmascript spec and it's pretty straightforward if a bit, umm...stilted...in it's wording.

http://ecma-international.org/ecma-262/5.1/#sec-15.4.4.20

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense

Lumpy posted:

Until it isn't.

Turns out they're deprecating that, their documentation already says you can't even though you still can. Oh well. It seems plain to me that sometimes you need a mutation and sometimes you don't.

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe

Thermopyle posted:

Yes.

Read through the ecmascript spec and it's pretty straightforward if a bit, umm...stilted...in it's wording.

http://ecma-international.org/ecma-262/5.1/#sec-15.4.4.20

That's just how spec-ese reads. Honestly, I like it, it tends to be pretty explicit.

twig1919
Nov 1, 2011
I am an inconsiderate moron whose only method of discourse is idiotic personal attacks.
I have been brought in to save the bacon for a startup that has been around for a couple years and has no (ok like 5%) test coverage for their high-risk product.
It's important enough that people could indirectly die (or be maimed) if the product fucks up, but its not controlling any important real-time applications.
So I can be a little fast and loose in order to get things tested, but I reaaaallllyyyyy should be adding tests before making any significant changes.

I am struggling with adding tests to their code without complete rewrites of everything piece by piece.
I am familiar with the usual methods of salvaging code like refactoring out hard dependencies using dependency injection, introducing common design patterns, and separating component responsibilities.

However, I am facing a couple unique javascript challenges.

I am trying to test a file that was written like this:

My Module
code:
export function A(); // do database stuff

export function B(); // do different database stuff

export function AorB(callA = false) { // dumb function that only exists because I refactored the unrelated behaviors of A and B into separate functions
   if (callA) {
      A()
    }else {
      B()
    }
};
My Test
code:
it('calls the stub', function()) {
  let stub = sinon.stub(myModule, 'A');
  myModule.AorB();
  // stubbed A not called, real A implementation is called.
  myModule.A()
 // stub is called
});
The problem I have is that I am trying to use Sinon to stub the functions A or B in order to test AorB, but this doesn't work because SinonJS mocks the export, but not the internal function references.
This seems like a problem unique to javascript because most other languages enforce the usage of Classes, so I would normally just extend the class with a Mock and call it a day.

I was thinking that if I refactored this module to use Classes then I wouldn't have this issue at all, but it would require changing hundreds of references in the code.
I was also thinking about trying to create export wrappers around the classes like so, but I have a feeling this won't work:
code:
const a = () => {//a}
export function A() {
  return a();
}
Ultimately, this entire module and most of the codebase will be refactored (re: rewritten) because it's a pile of untested garbage.

But in the short-term does anyone know a good solution around this issue that doesn't involve rewriting the entire module to make it testable?

In this case, I could just test AorB directly or just do the DB stuff in the AorB test, but this situation is definitely going to keep popping up on me as I work through this so I want something in my toolbox to deal with it effectively.

twig1919 fucked around with this message at 05:35 on Apr 14, 2020

ddiddles
Oct 21, 2008

Roses are red, violets are blue, I'm a schizophrenic and so am I
Redux is a lot easier to use now that it has hooks, but the huge boilerplate of creating actions/generators/reducers for everything makes it really cumbersome.

Then you find yourself wondering why one dispatch fired off a million state changes (its because you wrote bad code).

Then you just end up putting the user data into redux and then not bothering with anything else.

Anony Mouse
Jan 30, 2005

A name means nothing on the battlefield. After a week, no one has a name.
Lipstick Apathy

twig1919 posted:

I have been brought in to save the bacon for a startup that has been around for a couple years and has no (ok like 5%) test coverage for their high-risk product.
It's important enough that people could indirectly die (or be maimed) if the product fucks up, but its not controlling any important real-time applications.
So I can be a little fast and loose in order to get things tested, but I reaaaallllyyyyy should be adding tests before making any significant changes.

I am struggling with adding tests to their code without complete rewrites of everything piece by piece.
I am familiar with the usual methods of salvaging code like refactoring out hard dependencies using dependency injection, introducing common design patterns, and separating component responsibilities.

However, I am facing a couple unique javascript challenges.

I am trying to test a file that was written like this:

My Module
code:
export function A(); // do database stuff

export function B(); // do different database stuff

export function AorB(callA = false) { // dumb function that only exists because I refactored the unrelated behaviors of A and B into separate functions
   if (callA) {
      A()
    }else {
      B()
    }
};
My Test
code:
it('calls the stub', function()) {
  let stub = sinon.stub(myModule, 'A');
  myModule.AorB();
  // stubbed A not called, real A implementation is called.
  myModule.A()
 // stub is called
});
The problem I have is that I am trying to use Sinon to stub the functions A or B in order to test AorB, but this doesn't work because SinonJS mocks the export, but not the internal function references.
This seems like a problem unique to javascript because most other languages enforce the usage of Classes, so I would normally just extend the class with a Mock and call it a day.

I was thinking that if I refactored this module to use Classes then I wouldn't have this issue at all, but it would require changing hundreds of references in the code.
I was also thinking about trying to create export wrappers around the classes like so, but I have a feeling this won't work:
code:
const a = () => {//a}
export function A() {
  return a();
}
Ultimately, this entire module and most of the codebase will be refactored (re: rewritten) because it's a pile of untested garbage.

But in the short-term does anyone know a good solution around this issue that doesn't involve rewriting the entire module to make it testable?

In this case, I could just test AorB directly or just do the DB stuff in the AorB test, but this situation is definitely going to keep popping up on me as I work through this so I want something in my toolbox to deal with it effectively.
I'm not familiar with sinon, I'm more familiar with jest class mocks.

My somewhat naive gut impulse (without knowing much else about what all you got going on) is to break A, B, and AorB into three separate files. AorB.js will import A.js and B.js from elsewhere. And since they're now imports, in your AorB test file you can jest.mock the imports for A or B and have separate unit tests for those two separately. I've used jest.mock a fair amount and I'm pretty sure it mocks internal function calls from imports just fine.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



IMO get rid of AorB and use if statements at caller sites. Call sites should be statically verifiable since they're module imports, if I'm remembering the rules for those correctly.

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense

Anony Mouse posted:

I'm not familiar with sinon, I'm more familiar with jest class mocks.

My somewhat naive gut impulse (without knowing much else about what all you got going on) is to break A, B, and AorB into three separate files. AorB.js will import A.js and B.js from elsewhere. And since they're now imports, in your AorB test file you can jest.mock the imports for A or B and have separate unit tests for those two separately. I've used jest.mock a fair amount and I'm pretty sure it mocks internal function calls from imports just fine.

This was my instinct as well in fact I don't think you can test functions inside of a file, you're not really supposed to be doing that anyway. The only way I know of that you could would be to put them in a struct/object and then change the members of the struct/object from inside of the tests but that might be unnecessarily messy.

At the very least I'd make AB.js which exports both, along with all the shared crap they probably both use, as the OP said they were clumped together. Then you could mock them pretty easily. I definitely wouldn't refactor into classes though OP, these are functions right? You're not going to make a javascript singleton right?

Nolgthorn fucked around with this message at 13:38 on Apr 14, 2020

twig1919
Nov 1, 2011
I am an inconsiderate moron whose only method of discourse is idiotic personal attacks.

Munkeymon posted:

IMO get rid of AorB and use if statements at caller sites. Call sites should be statically verifiable since they're module imports, if I'm remembering the rules for those correctly.

Yes this is the ultimate plan, the only issue is that the callers are at 0% test coverage. So I don't want to gently caress with them yet, but I want to set them up for refactoring after adding tests.
Intelli-sense does not work for these because the way they are imported is something like as follows

mainSQLStuff
code:
import * as sqlStuff1 from 'sqlStuff1'
import * as sqlStuff2 from 'sqlStuff2'

export default {
  sqlStuff1: sqlStuff1,
  sqlStuff2: sqlStuff2
}
Then its used by calling mainSQLStuff.sqlStuff1.method() after importing mainSQLStuff.
So for some reason this causes intellisense to not calculate/recognize where any of the exports are used. Why they chose to do this is beyond my comprehension

Anony Mouse posted:

I'm not familiar with sinon, I'm more familiar with jest class mocks.

My somewhat naive gut impulse (without knowing much else about what all you got going on) is to break A, B, and AorB into three separate files. AorB.js will import A.js and B.js from elsewhere. And since they're now imports, in your AorB test file you can jest.mock the imports for A or B and have separate unit tests for those two separately. I've used jest.mock a fair amount and I'm pretty sure it mocks internal function calls from imports just fine.

This is a good idea. I will consider this to salvage something that's truly hosed.

Nolgthorn posted:

This was my instinct as well in fact I don't think you can test functions inside of a file, you're not really supposed to be doing that anyway.
Yes I agree but I am trying to fix this one piece at a time.

Nolgthorn posted:

I definitely wouldn't refactor into classes though OP, these are functions right? You're not going to make a javascript singleton right?

Here me out on the class thing because I don't intend to create a singleton.

These files export functions, but whether they are actually *just* functions is questionable to me.

This is how the file is setup:

My Module
code:
import db from './db'; // calls process.env a bunch when file is loaded
import config from './config' // calls process.env a bunch when file is loaded
import 'subqueries' from './subqueries // loads a bunch more methods

async function createX({x_id, x_name, ect...}, transaction = db) {
    // concatenate/generate a bunch of SQL stuff
    if(config.abc) {
     // do x SQL stuff
    }else {
     // do Y SQL stuff
    }
}

async function updateX({x_id, x_name, ect...}, transaction = db) {
    // concatenate/generate a bunch of SQL stuff
}

async function deleteX({x_id, x_name, ect...}, transaction = db) {
    // concatenate/generate a bunch of SQL stuff
}

async function getX({x_id, x_name, ect...}, transaction = db) {
    // concatenate/generate a bunch of SQL stuff
}
The average cyclomatic complexity is around 15 to generate an SQL statement... for a CRUD API.
So this is mainly happening because they are doing stuff like the following (which is what I am trying to fix):

code:
function createOrDelete(properties, toCreate) {
  if (toCreate) {
     // create SQL
  else (toDelete) {
    // delete SQL
  }
Then, they need to work these sql results so they have a big file that is also several thousand lines long that does stuff like this:

Other Module
code:
function convertDbRowToX(row) {
  return {
     id: row.id
     name: row.name
     someLongProperty: row.some_long_property
All they are really doing in these is converting the case, but inconsistently they do other stuff.
Also, they have business logic all over their controllers (complexity 30+ all over the place) which require updating constantly.

So to add 1 new (unrelated to existing functionality) property to a class, I'm looking at changing 15-20 files at the end of the day (to add the new property everywhere that table is mentioned).

So, I was thinking these should all be replaced with Classes that do something like this

My Model
code:
constructor(properties, db, config) {...}

function create(properties){...} // creates the object in the db

function save() {...} // saves the current object to db

function find(id) {...} // gets the object from the db by id

// All the other business logic that is scattered will live here.
So all the logic for a single item will ultimately be in one place instead of scattered over the universe. Additionally, I can fix the prop drilling issue where they are passing all the properties between every caller by hand {prop1, prop2, prop3, ect.}
Essentially this turns their SQL-ball into something more approximating a logical structure and the changes will be more localized to 1 place (the goal is to bring the change surface to 2-3 files for a new property: the model, vuex store, and front-end).


I also plan to add some dependency injection for things like databases, configs, ect. because all that stuff is loading stuff from the environment on import which makes testing it difficult.
The challenge I am having on that front is that importing things via dependency injection breaks VS-Code intelli-sense, which is annoying.

twig1919 fucked around with this message at 15:44 on Apr 14, 2020

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense
What you're describing at least in part is the Active Record Pattern. That's a perfectly good use of classes in my opinion, it's not really my favourite approach anymore, I think you might be setting yourself up for a lot of work. Don't low-ball your time estimate!

That changing the key case stuff is a pretty common mechanism, unfortunately it's repeated going one way and then back the other way often in many different places. I prefer having just a single case translator that's used for all tables, those two methods can be written in a few lines of code. But as it happens, the long form version gets screwed with over time and eventually it becomes impossible.

I've tried writing Active Record Patterns and it was time consuming because ideally you have the ability to just change some properties and call ".save()" but that's more complicated than it seems.

twig1919
Nov 1, 2011
I am an inconsiderate moron whose only method of discourse is idiotic personal attacks.

Nolgthorn posted:

What you're describing at least in part is the Active Record Pattern. That's a perfectly good use of classes in my opinion, it's not really my favourite approach anymore, I think you might be setting yourself up for a lot of work. Don't low-ball your time estimate!

That changing the key case stuff is a pretty common mechanism, unfortunately it's repeated going one way and then back the other way often in many different places. I prefer having just a single case translator that's used for all tables, those two methods can be written in a few lines of code. But as it happens, the long form version gets screwed with over time and eventually it becomes impossible.

I've tried writing Active Record Patterns and it was time consuming because ideally you have the ability to just change some properties and call ".save()" but that's more complicated than it seems.

Ah, I knew that there was a name for it but it wasn't coming to mind. Thanks for linking that!

And yeah, I am definitely leaning towards Laravel's Eloquent, which is pretty much the best Active Record system I have used.


In reality I am really just dealing with a pretty basic CRUD app (less than 100 tables) but then they need to add stuff like filters across and the whole things starts to explode into a mess when merging sql strings together.
IMO, they wrote most of this with an smart but inexperienced developer who chose the wrong abstraction over and over until the thing turned into a mess.

My main issue is that right now they are just trying to generate SQL which is turning into spaghetti in Javascript.
I am all ears for whatever I can do to recover their API logic into something sensible, what approach do you favor over Active Record now?

twig1919 fucked around with this message at 16:13 on Apr 14, 2020

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense
My main issue with active record, as is common with all software -- what you need it to do is never what you initially programmed it to do. When you start putting things into classes it all goes to hell. It's in my opinion probably the single biggest failure of object oriented programming as a whole. Because you think "yeah sure we'll just CRUD" but then you need a complex multi table query. Then you need sql transactions. Then you need to run sql on a different sever for one particular table. Then this then that.

Instead of ramming my head against a wall I just prefer to explicitly write everything these days. I start at surface level and I type a function that describes what I want to do "user_has_updated_their_billing_address()" or similar. Then I write that function and so on. If there's a point where I'm duplicating something more than twice I abstract away that single part. Like my example of changing string case on keys in an object, that's easy enough to do so it's an imported function now. When I use classes it's always really really sparing, I consider them only data containers 99% of the time.

I don't think code and data belongs in the same place.

But this is just m opinion. OOP certainly has been popular enough over the years.

twig1919
Nov 1, 2011
I am an inconsiderate moron whose only method of discourse is idiotic personal attacks.
Yes, this is typically my approach as well because the cost of the wrong abstraction is just so high: https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction.

I am dealing with a situation where there are so many wrong abstractions that I am reaching for the hammer solution to at least have only 1 wrong abstraction, but maybe this is a fundamentally flawed approach.

Thanks for the advice!

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!
Argh it's module hell again.

I have a Typescript thing that is working in nodejs.

I'm trying to also add a node-turn server to it. https://www.npmjs.com/package/node-turn

I've done yarn add node-turn which has done its thing.

There is no @types/node-turn.

node-turn's main file is of the form

code:
var server = function(config) {
...
}

module.exports = server;
Their directions say you use it like
code:
var Turn = require('node-turn');
var server = new Turn({
  // set options
  authMech: 'long-term',
  credentials: {
    username: "password"
  }
});
server.start();
I tried to make my own type file for this but I don't really know what I'm doing and all the instructions I could find are about how to do a type file for a JS class, which this isn't quite. My least unsuccessful attempt is
code:
declare module 'node-turn' {
  interface Turn {
    start(): void;
    stop(): void;
    addUser(username: string, password: string): void;
    removeUser(username: string): void;
  }
  function server(config: any): Turn;
  export = server;
}
I also added to my tsconfig.json "esModuleInterop": true,
maybe relevant bits under compilerOptions
code:
  	"target": "es6",
    "esModuleInterop": true,
    "moduleResolution": "node",
  	"module": "esnext",
I also tried the .d.ts file declaring Turn as a class with a constructor. Either of these ways served to make the compiler not complain any more, but in both cases the outcome when I try to run it is
code:
[1]             this.turnServer = new node_turn__WEBPACK_IMPORTED_MODULE_3__["Turn"]({
[1]                               ^
[1]
[1] TypeError: node_turn__WEBPACK_IMPORTED_MODULE_3__.Turn is not a constructor
Oh, and I'm importing it like
code:
import Turn from 'node-turn';
There's so many moving parts here and it feels like nobody knows how they're supposed to come together. Any help?

Anony Mouse
Jan 30, 2005

A name means nothing on the battlefield. After a week, no one has a name.
Lipstick Apathy
Got a code repo you can point us at?

Osmosisch
Sep 9, 2007

I shall make everyone look like me! Then when they trick each other, they will say "oh that Coyote, he is the smartest one, he can even trick the great Coyote."



Grimey Drawer
There's always the option of just using the node-turn part as regular JS? Create your own wrapper interface with some any type overrides if you want to isolate that part from your typed code.

I may be missing something though, I'm only getting started with this stuff myself.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Osmosisch posted:

There's always the option of just using the node-turn part as regular JS? Create your own wrapper interface with some any type overrides if you want to isolate that part from your typed code.

I may be missing something though, I'm only getting started with this stuff myself.
I tried using it through an 'any' as well. The problem is I don't seem to be able to get module.export to map to any kind of import that works in Typescript.

I might just go with running the TURN stuff as a completely separate executable, that way I don't have to get my package configuration to pair up with a seemingly incompatible package configuration.

Ah! I found my problem, it was something completely different - "yarn start" wasn't performing a rebuild unless it succeeded, after which it would do a hot reload. So I had a mistake in one of the versions then every attempt to try something different didn't actually even try, and just returned the same error. Sorry for the waste of time question! (The fix being to "yarn build" before "yarn start" again.)

Edit: for completeness' sake, the answer was yes I did need "esModuleInterop": true in tsconfig, the correct import line was import Turn from 'node-turn';, and the type declaration file that worked was
code:
declare module 'node-turn' {
  class Turn {
    constructor(config: any);
    public start(): void;
    public stop(): void;
    public addUser(username: string, password: string): void;
    public removeUser(username: string): void;
  }
  export = Turn;
}

roomforthetuna fucked around with this message at 00:05 on Apr 17, 2020

necrotic
Aug 2, 2005
I owe my brother big time for this!
Try using import lib = require('package');. This is a special form in typescript for CJS packages.

https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require

E: whoops, missed your edit

Kraus
Jan 17, 2008
A few years ago, after a drunken conversation at DragonCon, I ended up writing a little script to output every possible syllable of the Klingon language. The surrounding HTML uses the <pre> tag so I could use document.writeln(). The idea here is that every logical combination of onset, nucleus, and coda is legit, except ones that contain the substrings "ow" or "uw". (The empty string at the beginning of the codas array is make creating the syllables that are just consonant-vowel easier.)

code:

var onsets = ["p", "t", "q", "'", "b", "D", "tlh", "ch", "Q", "j", "S", "H", "v", "gh", "m", "n", "ng", "w", "r", "l", "y"];

var nuclei = ["a", "e", "I", "o", "u"];

var codas = ["" , "p", "t", "q", "'", "b", "D", "tlh", "ch", "Q", "j", "S", "H", "v", "gh", "m", "n", "ng", "w", "r", "l", "y", "w'", "y'", "rgh"];


for(o of onsets){

	for (n of nuclei) {
    
    	for (c of codas) {
        
          (n=="o"||"u")&&c[0]=="w"||document.writeln(o+n+c)
        
        }
    }
}


So, my little question is this: A friend pointed out that you could also do (n>"o"||"u"). I'm guessing this is because the Unicode values for "a", "e", and "I" are greater less than the values for "o" and "u", or is there some weird logical thing I'm missing?

Kraus fucked around with this message at 17:55 on May 5, 2020

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



code:
> var n = "n";
> n=="o"||"u"
< "u"
Which is to say that n=="o"||"u" is not doing what you seem to think it is and, if your code works correctly, I think it's by accident rather than design.

n=="o"||"u" evaluates as (n=="o")||"u" because of the operator precedence, so it's checking the nuclei against 'o' then, if that's false, evaluating whether a string composed of the character u is truth-y, which it is. Your friend was right in that it doesn't matter what comparison you do, the expression inside those parenthesis always evaluates to true.

e: I suspect what you meant was (n == 'o' || n == 'u')

Munkeymon fucked around with this message at 18:01 on May 5, 2020

Roadie
Jun 30, 2013

Munkeymon posted:

code:
> var n = "n";
> n=="o"||"u"
< "u"
Which is to say that n=="o"||"u" is not doing what you seem to think it is and, if your code works correctly, I think it's by accident rather than design.

n=="o"||"u" evaluates as (n=="o")||"u" because of the operator precedence, so it's checking the nuclei against 'o' then, if that's false, evaluating whether a string composed of the character u is truth-y, which it is. Your friend was right in that it doesn't matter what comparison you do, the expression inside those parenthesis always evaluates to true.

e: I suspect what you meant was (n == 'o' || n == 'u')


It's also a good opportunity to try out map/filter stuff.

JavaScript code:
const ONSETS = ["p", "t", "q", "'", "b", "D", "tlh", "ch", "Q", "j", "S", "H", "v", "gh", "m", "n", "ng", "w", "r", "l", "y"];
const NUCLEIS = ["a", "e", "I", "o", "u"];
const CODAS = ["", "p", "t", "q", "'", "b", "D", "tlh", "ch", "Q", "j", "S", "H", "v", "gh", "m", "n", "ng", "w", "r", "l", "y", "w'", "y'", "rgh"];

/**
 * Given three arrays, generate a new array containing every permutation of the combination of items from those three arrays.
 * @template A
 * @template B
 * @template C
 * @param {A[]} a
 * @param {B[]} b
 * @param {C[]} c
 * @returns {[A, B, C][]}
 */
const generatePermutations = (a, b, c) =>
  a.flatMap((aItem) =>
    b.flatMap((bItem) => c.map((cItem) => [aItem, bItem, cItem]))
  );

generatePermutations(ONSETS, NUCLEIS, CODAS)
  .filter(
    ([onset, nuclei, coda]) =>
      (nuclei !== "o" && nuclei !== "u") || !coda.startsWith("w")
  )
  .forEach(([onset, nuclei, coda]) => document.writeln(onset + nuclei + coda));
Note that flatMap doesn't work in IE 11, but should in every other current browser.

Kraus
Jan 17, 2008
Thanks for all the replies, y'all!

As a side note, that friend and I managed to get the one liner inside the loops down to this:

n>"n"&c[0]=="w"||document.writeln(o+n+c)

This abuses both operator precedence and the fact that the Unicode values of "o" and "u" are greater than the Unicode value for "n".

Edit: It also abuses the fact that the > and the == get evaluated to boolean values, which the bitwise AND can deal with.

Kraus fucked around with this message at 21:58 on May 5, 2020

Adbot
ADBOT LOVES YOU

Boosh!
Apr 12, 2002
Oven Wrangler
I am moving my Contentful API calls from my React app to my Express API, merely wrapping my Contentful calls in routes:

code:
app.get('/getAll, (req, res) => {

    client.getEntries({
        content_type: 'presentation',
	limit: 30,
        skip: 0,
    })
    .then((response) => {
	console.log(response)
        res.json(response)
    })

})
With the limit set to 30 it works, but if I bump the limit to 60 or so I get an ERR_EMPTY_RESPONSE error message in my console (doesn't work in Postman either). This was working perfectly fine in my React app but once I moved the calls to Express I started to have this problem. The payload is pretty small, about 60kb and I am able to log it right before sending it. Any idea why this is happening?

EDIT: Seems like any response over 60kb is having this issue vs limit size.

Boosh! fucked around with this message at 14:54 on May 7, 2020

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply