<commit_before>'use strict';


var CtrlProto;
var forIn              = require('./utils/forIn');
var webRootHandlers    = require('./entryHandlers/web-root');
var normalizeEntryName = require('./helpers').normalizeEntryName;

var hasOwn = Object.prototype.hasOwnProperty;

var starterList    = ['_', '.'];
var starterListLen = starterList.length;



function shouldBeIgnored (entry) {
	if ( matchStartWith(entry) ) return true;

	return false;
}



function matchStartWith (name) {
	var starter, starterLen, nameStart, i;

	for (i = 0; i < starterListLen; i+=1) {
		starter    = starterList[i];
		starterLen = starter.length;
		nameStart  = name.substr(0, starterLen);
		
		if (nameStart === starter) {
			return true;
		}
	}

	return false;
}



var has = {
	verbs: function hasVerbs (ctrl) {
		 /*
		 | because ctrl.verbs also contain 'noVerb'
		*/
		var verbs = ctrl.verbs;

		if (verbs.get || verbs.post || verbs.put || verbs.delete){
			return true;
		}

		return false;
	},

	methods: function hasMethods (ctrl) {
		var sharedMethods;
		
		var ownMethods = (Object.keys (ctrl.methods).length > 0);

		if (ctrl.global._shared_methods) {
			sharedMethods = (Object.keys (ctrl.global._shared_methods).length > 0);
		}
		
		return ownMethods || sharedMethods;
	},

	subCtrls: function hasSubCtrls (ctrl) {
		var sharedCtrls;
		
		var ownCtrls = (Object.keys (ctrl.subCtrls).length > 0);

		if (ctrl.global._shared_ctrls) {
			sharedCtrls = (Object.keys (ctrl.global._shared_ctrls).length > 0);
		}
		
		return ownCtrls || sharedCtrls;
	}
};



/*
  ┌─────────────────────────────────────────────────────────────────────
  │ 	run = {
  │		.verb()    - on request (in chain)
  │		.method()  - on request (in chain)
  │		.subCtrl() - on request (in chain)
  │	}
  │	all 3 are being pushed to chains and called with: .call(ctrl, io)
 */
var run = {
	verb: function (io) {
		var verbs = this.verbs;
		var verb  = verbs[io.req.method.toLowerCase()];

		if (verb) {
			verb.call(this, io);
		}
		else if (verbs.noVerb) {
			verbs.noVerb.call(this, io);
		}
		else {
			this.next(io);
		}
	},

	method: function (io) {
		var next   = io.params[0];
		var method = this.methods[next.toLowerCase()];

		if (method) {
			io.params.shift();

			method.call(this, io);
		}

	},

	subCtrl: function (io) {
		var subCtrl;
		var next = io.params[0];

		subCtrl = this.subCtrls[next.toLowerCase()];

		if (subCtrl) {
			subCtrl.parent = this;
			subCtrl.checkIn(io);
		} 
	}
};



/*
 ┌────────────────────────────────────────────────
 │ ctrl.id is its parent.id + /name.
 │ id is unique, name isn't.
 │ examples:
 │     id:   '/foo' │ '/foo/bar' │ '/foo/bar/foo'
 │     name: 'foo'  │ 'bar'      │ 'foo'
*/
function setID (ctrl) {
	var buildID = '';

	// is RC? id = '/'
	if (ctrl.isRC) return '/';

	// RC's direct child - avoid double slashes
	if (ctrl.parent && !ctrl.parent.isRC) buildID += ctrl.parent.id;

	// all
	buildID += '/' + ctrl.name;

	return buildID; 
}



/*
 ┌─────────────────────────────────────────
 │ this method gets a folder-map obj and sets the ctrl with its different props and
 │ functionalities.
 │ forEach entry in the folder:
 │   ignore _
 │   handle if has an entryHandler
 │     or 
 │   set as a ctrl's method/subCtrl
*/
function parseFolderMap (ctrl, folderMap) {
	forIn(folderMap.entries, function (key, entryMap) {
		var isFile, name;

		// normalizeEntryName = lowerCase, remove extension
		name = normalizeEntryName(key, isFile);

		/* Deal Breaker */ if (shouldBeIgnored(name)) {return;} // ignore preceding '_'
		
		isFile = entryMap.type;

		// if such entryHandler exists
		if (hasOwn.call(webRootHandlers, name)) {
			webRootHandlers[name].call(ctrl, entryMap);
		}
		else {
			if (isFile) {
				ctrl.methods[name] = require(entryMap.path);
			}
			else {
				ctrl.subCtrls[name] = entryMap;
			}
		}
	}); 
}



/*
  ┌──────────────────────────────────────────────────────────────────────────────────────────
  │ delegated means when exists in a ctrl, all of its sub-ctrl will get it too (inheritance)
 */
function delegateNoVerb (ctrl) {
	var verbs = ctrl.verbs;

	// if doesn't own: take from parent
	if (!verbs.noVerb && ctrl.parent) {
		verbs.noVerb = ctrl.parent.verbs.noVerb;
	}
}



/*
  ┌────────────────────────────────────────────────────────────────────────────────────────────<commit_msg>added a duplicate check in parseFolder fn<commit_after>'use strict';


var CtrlProto;
var forIn              = require('./utils/forIn');
var webRootHandlers    = require('./entryHandlers/web-root');
var normalizeEntryName = require('./helpers').normalizeEntryName;

var hasOwn = Object.prototype.hasOwnProperty;

var starterList    = ['_', '.'];
var starterListLen = starterList.length;


function checkDuplicates (ctrl) {
	var method, i;
	var methods  = Object.keys(ctrl.methods);
	var subCtrls = Object.keys(ctrl.subCtrls);
	var size     = methods.length;

	for (i = 0; i < size; i+=1) {
		method = methods[i];

		if ( ~subCtrls.indexOf(method) ) {
			console.log('Bootstruct Warning:');
			console.log('   You have a controller and a method with the same name ('+ method +') in:' + ctrl.folderMap.path);
		}
	}
}



function shouldBeIgnored (entry) {
	if ( matchStartWith(entry) ) return true;

	return false;
}



function matchStartWith (name) {
	var starter, starterLen, nameStart, i;

	for (i = 0; i < starterListLen; i+=1) {
		starter    = starterList[i];
		starterLen = starter.length;
		nameStart  = name.substr(0, starterLen);
		
		if (nameStart === starter) {
			return true;
		}
	}

	return false;
}



var has = {
	verbs: function hasVerbs (ctrl) {
		 /*
		 | because ctrl.verbs also contain 'noVerb'
		*/
		var verbs = ctrl.verbs;

		if (verbs.get || verbs.post || verbs.put || verbs.delete){
			return true;
		}

		return false;
	},

	methods: function hasMethods (ctrl) {
		var sharedMethods;
		
		var ownMethods = (Object.keys (ctrl.methods).length > 0);

		if (ctrl.global._shared_methods) {
			sharedMethods = (Object.keys (ctrl.global._shared_methods).length > 0);
		}
		
		return ownMethods || sharedMethods;
	},

	subCtrls: function hasSubCtrls (ctrl) {
		var sharedCtrls;
		
		var ownCtrls = (Object.keys (ctrl.subCtrls).length > 0);

		if (ctrl.global._shared_ctrls) {
			sharedCtrls = (Object.keys (ctrl.global._shared_ctrls).length > 0);
		}
		
		return ownCtrls || sharedCtrls;
	}
};



/*
  ┌─────────────────────────────────────────────────────────────────────
  │ 	run = {
  │		.verb()    - on request (in chain)
  │		.method()  - on request (in chain)
  │		.subCtrl() - on request (in chain)
  │	}
  │	all 3 are being pushed to chains and called with: .call(ctrl, io)
 */
var run = {
	verb: function (io) {
		var verbs = this.verbs;
		var verb  = verbs[io.req.method.toLowerCase()];

		if (verb) {
			verb.call(this, io);
		}
		else if (verbs.noVerb) {
			verbs.noVerb.call(this, io);
		}
		else {
			this.next(io);
		}
	},

	method: function (io) {
		var next   = io.params[0];
		var method = this.methods[next.toLowerCase()];

		if (method) {
			io.params.shift();

			method.call(this, io);
		}

	},

	subCtrl: function (io) {
		var subCtrl;
		var next = io.params[0];

		subCtrl = this.subCtrls[next.toLowerCase()];

		if (subCtrl) {
			subCtrl.parent = this;
			subCtrl.checkIn(io);
		} 
	}
};



/*
 ┌────────────────────────────────────────────────
 │ ctrl.id is its parent.id + /name.
 │ id is unique, name isn't.
 │ examples:
 │     id:   '/foo' │ '/foo/bar' │ '/foo/bar/foo'
 │     name: 'foo'  │ 'bar'      │ 'foo'
*/
function setID (ctrl) {
	var buildID = '';

	// is RC? id = '/'
	if (ctrl.isRC) return '/';

	// RC's direct child - avoid double slashes
	if (ctrl.parent && !ctrl.parent.isRC) buildID += ctrl.parent.id;

	// all
	buildID += '/' + ctrl.name;

	return buildID; 
}



/*
 ┌─────────────────────────────────────────
 │ this method gets a folder-map obj and sets the ctrl with its different props and
 │ functionalities.
 │ forEach entry in the folder:
 │   ignore _
 │   handle if has an entryHandler
 │     or 
 │   set as a ctrl's method/subCtrl
*/
function parseFolderMap (ctrl, folderMap) {
	forIn(folderMap.entries, function (key, entryMap) {
		var isFile, name;

		// normalizeEntryName = lowerCase, remove extension
		name = normalizeEntryName(key, isFile);

		/* Deal Breaker */ if (shouldBeIgnored(name)) {return;} // ignore preceding '_'
		
		isFile = entryMap.type;

		// if such entryHandler exists
		if (hasOwn.call(webRootHandlers, name)) {
			webRootHandlers[name].call(ctrl, entryMap);
		}
		else {
			if (isFile) {
				ctrl.methods[name] = require(entryMap.path);
			}
			else {
				ctrl.subCtrls[name] = entryMap;
			}
		}
	});

	checkDuplicates(ctrl);
}



/*
  ┌──────────────────────────────────────────────────────────────────────────────────────────
  │ delegated means when exists in a ctrl, all of its sub-ctrl will get it too (inheritance)
 */
function delegateNoVerb (ctrl) {
	var verbs = ctrl.verbs;

	// if doesn't own: take from parent
	if (!verbs.noVerb && ctrl.parent) {
		verbs.noVerb = ctrl.parent.verbs.noVerb;
	}
}



/*
  ┌────────────────────────────────────────────────────────────────────────────────────────────
{'fraction_non_alphanumeric': 0.21129226392384287, 'fraction_numerical': 0.002407265565160302, 'mean_word_length': 3.2452392011147237, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6735818', 'n_tokens_mistral': 3771, 'n_tokens_neox': 3153, 'n_words': 816}