rm -rf /usr/local/lib/node_modules brew uninstall node brew install node --without-npm echo prefix=~/.node >> ~/.npmrc sudo chown -R $USER ~/.npm sudo chown -R $USER /usr/local/lib/node_modules curl -L https://www.npmjs.com/install.sh | sh sudo chown -R $USER:$GROUP ~/.npm npm cache clean
Set up Sublime, Homebrew, Ruby, Rails, Postgres
New Installs for Mac 10.10 Yosemite
Install Sublime
open ~/.bash_profile
If the following is not there already, add
export PATH=/usr/local/bin:$PATH
Create softlink from app to bin to use sublime in terminal
! the softlink name determines the terminal command name
sublime .
opens current folder as project in Sublime
Note: Check the name of the app in Applications, update as needed
ln -s /Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl /usr/local/bin/sublime
Install homebrew
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Install Ruby 2.2 / rbenv
Install rbenv
brew install rbenv ruby-build
Add rbenv to bash so that it loads every time you open a terminal
echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile source ~/.bash_profile
Install Ruby
rbenv install 2.2.2 rbenv global 2.2.2 ruby -v
Install Rails
gem install rails -v 4.2.1
Update rbenv to see new rails
rbenv rehash
Install PostGres
brew install postgresql
Softlink Postgres to User LaunchAgents folder to start on signin
mkdir -p ~/Library/LaunchAgents ln -sfv /usr/local/opt/postgresql/*.plist ~/Library/LaunchAgents/
start postgres / restart or use the following
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist
Create a rails app to test
If you want to use Postgres
Note you will need to change config/database.yml’s username to be
the same as your OSX user account. (for example, mine is ‘chris’)
rails new myapp -d postgresql
Move into the application directory
cd myapp
Create the database
rake db:create
if you get the following error
connections on Unix domain socket “/tmp/.s.PGSQL.5432”?
remove the postgres gem and use bundle install to reinstall the correct version
gem uninstall pg bundle install rake db:create rails server
Use Puma as WebServer
gem install puma
Add this to the Gemfile
\#use puma webserver gem 'puma'
Run Bundle install
bundle install
created via trail by fire and a handful of sites including
https://gorails.com/setup/osx/10.10-yosemite
Gitignore template and Git Remove .DS_Store from commits.
.gitignore
/.buildpath /build/ */archive/ __MACOSX .DS_Store .project .settings .classpath .sass-cache/ # OS generated files # ###################### */.DS_Store .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes Icon? ehthumbs.db Thumbs.db # Packages # ############ # it's better to unpack these files and commit the raw source # git has its own built in compression methods *.7z *.dmg *.gz *.iso *.jar *.rar *.tar *.zip # Logs and databases # ###################### *.log *.sql *.sqlite # Modules, components, dist, tmp # ################################## node_modules bower_components dist .tmp .sass-cache
This is nice to remember.
$ git rm --cached .DS_Store
AngularJS – State Service
‘use strict’;
/**
* @ngdoc service
* @name interceptcms.stateService
* @description
* # stateService
* Factory in the interceptcms.
*/
function stateHistory($state) {
var history = [];
var idx = 0;
// function loadPrevious(idx) {
// $state.go(history[idx].state, history[idx].params);
// }
function addState() {
history.push({state: $state.current, params: $state.params});
idx += 1;
}
return {
loadListView: function(model) {
for (var i = history.length - 1; i >= 0; i--) {
if (history[i].params.id === undefined && history[i].params.model === model) {
$state.go(history[i].state, history[i].params);
}
}
},
loadRecordView: function(model, id) {
var params = {};
params.id = id.replace('/', '');
params.model = model;
$state.go("/:model/:id", params);
//$state.transitionTo("/:model/:id", params, { reload: true, inherit: true, notify: true });
},
// FORM SUBMISSION CREATES AN ADDITIONAL STATE
goBack: function (model, createEdit, id) {
//loadListView($state.params.model);
if (id) {
this.loadRecordView(model, id);
}
},
stateLoaded: function () {
addState();
},
listView: function(model, id) {
if (!id) {
this.loadListView(model);
} else {
this.loadRecordView(model, id);
}
}
};
}
angular.module(‘interceptcms’)
.factory(‘stateService’, stateHistory);
AngularJS – State Model Data Service
‘use strict’;
/**
* @ngdoc service
* @name interceptcms.config
* @description
* # config
* Factory in the interceptcms.
*/
var stateModelDataService = function ($state, dataService, modelService, RESOURCES) {
var setConfig = function(query, params) {
var config = {};
config.searchField = query.field || 'tag';
config.criteria = decodeURIComponent(params.id) || "";
config.skip = query.skip || 0;
config.limit = query.limit || RESOURCES.LIMIT;
config.dir = query.dir || 'asc';
config.sort = {};
config.query = {};
if (config.query.sort) {
config.sort[config.query.sort] = config.dir;
} else {
config.sort[primaryField] = config.dir;
}
return config;
};
var isUpdate = function (array) {
return (array.indexOf(RESOURCES.EDIT) > -1) ? true : false;
};
var isCreate = function (array) {
return (array.indexOf(RESOURCES.CREATE) > -1) ? true : false;
};
return function (callback, dataFiltered, setScopeValue) {
var urlArray = [];
// CONTROLLER VARIABLES
// DYNAMIC URL
if ($state.params.model) {
urlArray.push($state.params.model);
if ($state.params.view) {
urlArray.push($state.params.view);
}
if ($state.params.id) {
urlArray.push($state.params.id);
}
} else {
// EXPLICIT URL
urlArray = $state.current.name.split('/');
}
// SET UP CONFIG
// DEFINE MODEL / ISCREATE / ISUPDATE / ID / BASEURL
var qs = window.location.href.split('?')[1];
var config = {};
config.model = {};
config.model._modelName = urlArray[0];
config.isCreate = isCreate(urlArray);
config.isUpdate = isUpdate(urlArray);
config.id = ($state.params.id) ? "/" + $state.params.id : "";
config.baseUrl = RESOURCES.API_URL + urlArray[0];
config.search = ($state.params.search) ? "/search/" + $state.params.search : "";
config.qs = (qs) ? "?" + qs : "";
if (config.search === "") {
config.qs = config.qs.replace("&dir=", " ");
}
// REQUEST AND RETURN MODEL DEFINITION
// PASSED TO CONTROLLER FUNCTION setScopeValue
if (config.model._modelName !== "") {
modelService.requestModel(config.model._modelName, setScopeValue);
}
// RETURN CONFIG TO CONTROLLER / CALLBACK
callback(config);
// RETURN DATA TO DATAFILTERED FOR OPTIONAL LOCAL FILTERING
if (!config.isCreate && config.model._modelName !== "") {
dataService.getData(config.baseUrl + config.id + config.search + config.qs, config.model, dataFiltered);
}
};
};
angular.module(‘interceptcms’)
.factory(‘stateModelDataService’, stateModelDataService);
AngularJS – Data Service
‘use strict’;
/**
* @ngdoc service
* @name interceptcms.dataService
* @description
* # dataService
* Service in the interceptcms.
*/
angular.module(‘interceptcms’)
.factory(‘dataService’, function($http, $state, $log, alert, stateService, authToken, utils, socketService, sailsValidation, RESOURCES) {
// USE SOCKET IO TO SEND OUT NOTIFICATION OF RECORD UPDATE
function sendNotification (user, room, msg, link) {
socketService.emit('message', user, { room: room, msg: msg + ": " + link});
}
// CREATE NOTIFICATION RECORD IN THE DB
// SEND NOTIFICATION TO USER
function postNotificationToDB(APP_URL, model, room, action, title, id, updatedValues, user) {
var notification = {
model: model,
action: action,
title: title,
updatedValues: updatedValues.join(','),
link: "<a href='" + APP_URL + "#/" + model + id + "'>Link</a>",
user: user
};
// TO DO GET LIST OF USERS WATCHING ACTIONS IN DB
// SEND AS AN ARRAY IN THE QUERY STRING TO POST NOTIFICATIONS TO DB
var user = authToken.getUsername();
// SEND NOTIFICATION TO USERS
//$http.post(RESOURCES.API_URL + 'notification/?user=' + user, notification).success(function(data) {
$http.post(RESOURCES.API_URL + 'notification/', notification).success(function(data) {
var notificationRoom = room + ": " + model;
var notificationMsg = model + " " + action + " '" + title + "' ";
getUsersToNotify("admin", notificationRoom, notificationMsg, notification.link, data.id);
});
}
function getUsersToNotify(type, room, msg, link, id) {
$http.get(RESOURCES.API_URL + 'user/?userType=' + type).success(function(data) {
for (var i = data.length - 1; i >= 0; i--) {
var tmpObj = {notification: id, user: data[i].name};
$http.post(RESOURCES.API_URL + 'usernotification/', tmpObj);
}
for (var i = data.length - 1; i >= 0; i--) {
sendNotification(data[i].name, room, msg, link);
};
});
}
// RETURNED OBJECT
var dataService = {};
// SET NOTIFICATION TYPE
var room = 'notifications';
// GET DATA FROM API
dataService.getData = function (url, model, callback) {
$http.get(url).success(function(obj) {
// ADD NEW STATE TO HISTORY
stateService.stateLoaded();
return callback(obj, model);
}).error(function(err){
alert('warning', "Unable to get " + model + ".", err.message);
});
};
dataService.confirmDelete = function(model, id, user) {
var answer = window.prompt("Are you sure you want to delete this record? \r Type 'y' to delete.");
if (answer === 'y') {
dataService.deleteRecord(model, id, user);
}
};
dataService.deleteRecord = function(model, id, user) {
// SET MSG VARIABLES
var title = "";
var updatedValues = [];
// RECORD DB NOTIFICATION TO USER
// SEND NOTIFICATION TO USER
postNotificationToDB(RESOURCES.APP_URL, model, room, "Deleted", title, id, updatedValues, user);
$http.get(RESOURCES.API_URL + model + "/destroy/" + id);
stateService.listView(model);
};
// EXTERNAL MODELS ARE QUERIED FOR MODEL PRIMARY FIELD AND ID
// THIS DATA IS USED WHEN CREATING DROPDOWNS IN THE UI
dataService.getRelatedData = function(val, model, callback) {
$http.get(RESOURCES.API_URL + model + "/get").success(function(obj) {
val = "dd_" + val;
return callback(val, obj);
}).error(function(err){
alert('warning', "Unable to get " + model.trim() + ".", err.message);
});
};
// STRIP OUT UNEDITED PROPERTIES BEFORE SUBMITTING
// ALLOWS US TO TRACK MODIFICATIONS
// IF THE OBJECT IS MODIFIED SUBMIT / ELSE SEND ALERT TO USER
dataService.processBeforeSubmitting = function(originalObj, editedObj) {
var obj = utils.compareObjectsKeepChanges(originalObj, editedObj);
if (obj) {
submit(obj);
} else {
alert('warning', "No changes were found.");
}
};
// SUBMIT DATA TO API
function submit(obj) {
var user = authToken.getUsername();
// SET UP
var model = ($state.params.model) ? $state.params.model : $state.current.name.split('/')[0];
var url = RESOURCES.API_URL + model;
var id = ($state.params.id) ? '/' + $state.params.id : "";
var submittedMsg = (id) ? "Updated" : "Created";
var createEdit = true;
// REMOVE RECORD KEEPING ELEMENTS FROM OBJECT
obj = utils.stripObj(obj, RESOURCES.RECORD_KEEPING_DATA);
// SET NOTIFICATION TO SPECIFY UPDATED PROPERTIES
var updatedValues = [];
if (id) {
for (var k in obj) {
updatedValues.push(k);
}
}
// UPDATE EXISTING OBJECT
// TO INCLUDE UPDATED OR CREATED BY
if (id) {
url += id;
obj.updatedBy = user;
} else {
obj.createdBy = user;
}
// POST DATA TO API
$http.post(RESOURCES.API_URL + model + id, obj).success(function(data) {
// SET MSG VARIABLES
var title = (obj[model] !== undefined) ? obj[model] : obj.title;
// IF RECORD WAS CREATED
// GET AND SET NEW RECORDS ID
if (!id) {
id = "/" + data.id;
}
// SEND ALERT TO USER
alert("success", model + submittedMsg, "Thank you.");
// RECORD DB NOTIFICATION TO USER
// SEND NOTIFICATION TO USER
postNotificationToDB(RESOURCES.APP_URL, model, room, submittedMsg, title, id, updatedValues, user);
stateService.loadRecordView(model, id);
}).error(function (data) {
var err = {};
err.model = model;
if (data.raw) {
if (data.raw.code === 11000) {
err.msg = data.raw.err;
}
}
sailsValidation(err);
});
}
return dataService;
});
AngularJS – Model Service
‘use strict’;
/**
* @ngdoc service
* @name interceptcms.modelService
* @description
* # modelService
* Factory in the interceptcms.
*/
angular.module(‘interceptcms’)
.factory(‘modelService’, function($http, alert, RESOURCES) {
// LOAD MODELS ONCE AND SAVE TO cachedModels
var cachedModels = {};
// GET MODEL FROM API
// RETURNS OBJECT STRUCTURE AND DATA TYPES
// ADDS MODEL NAME PROPERTY
// PASSES TO AUGMENTMODEL
function getModel(modelName, callback) {
$http.get(RESOURCES.API_URL + modelName + "/model").success(function(obj) {
var model = {};
model = augmentModel(obj);
model._modelName = modelName;
if (callback) {
callback("model", model);
} else {
return model;
}
setModel(modelName, model);
}).error(function(err){
alert('warning', "Unable to get " + modelName + ".", err.message);
});
}
function setModel(modelName, model) {
cachedModels[modelName] = model;
}
// AUGMENTMODEL
// IDENTIFIES RECORD KEEPING DATA AS ARRAY
// IDENTIFIES RELATED MODELS WITHIN MODEL
// IDENTIFIES DROPDOWN MODELS WITHIN MODEL
// ADDS RESULT TO PROPERTY relatedModels
// UPDATE MODEL WITH GENERIC PROPERTIES
// CALLED BY GET MODEL
// ADDS VIEW SPECIFIC INFO
// TO GENERATE VIEW COMPONENTS FROM RELATED DATA SETS
function augmentModel(obj){
var relatedArray = [];
var dropdownArray = [];
for (var prop in obj) {
// TO DO REFACTOR RELATIONSHIP DESCRIPTION
// COLLECTION = ONE TO MANY
if (obj[prop].collection) {
// MONGO SPECIFIC
relatedArray.push(checkRelated(obj, prop, "collection", "collection"));
}
// MODEL = ONE TO ONE using VIA AS field REFERENCE
if (obj[prop].model) {
// REFACTOR
// USE GETDROPDOWNVALUES TO PROPULATE AS OBJECT
// WITH NAME PROPERTY & VALUE ARRAY
// MONGO SPECIFIC
dropdownArray.push(checkRelated(obj, prop, "model"));
}
}
obj._rkData = RESOURCES.RECORD_KEEPING_DATA;
obj._relatedModels = relatedArray;
obj._dropdownArray = dropdownArray;
return obj;
}
// CREATE RELATEDARRAY ELEMENTS
// CREATE dropdownArray ELEMENTS
// FOR VIEW
function checkRelated(obj, prop, type) {
//if (obj[prop][type]) {
var tmpObj = {};
tmpObj[prop] = obj[prop][type];
return tmpObj;
//}
}
var requestModel = function(modelName, callback) {
if (!cachedModels[modelName]) {
getModel(modelName, callback);
} else {
console.log("returning cached model:" + modelName);
return callback("model", cachedModels[modelName]);
}
};
var getCachedModel = function(modelName) {
var model = cachedModels[modelName];
var array = [];
for (var prop in model) {
array.push(prop);
}
return array;
};
var getDropDownModel = function(modelName) {
};
return {
requestModel: requestModel,
getCachedModel: getCachedModel,
getModel: getModel
};
});
AngularJS – Utilities
‘use strict’;
/**
* @ngdoc service
* @name interceptcms.utilities
* @description
* # utilities
* Service in the interceptcms.
*/
var initInjector = angular.injector([‘ng’]);
var $http = initInjector.get(‘$http’);
angular.module(‘interceptcms’).service(‘utils’, function () {
var utilities = {
// PRESUBMIT OBJECT MODIFICATION
// REMOVE UNCHANGED PROPERTIES BEFORE UPDATE
// ONLY SUBMIT CHANGED PROPS
// OBJECTS PROPERTIES ARE COMPARED TO THE ORIGINAL OBJECT
// ONLY CHANGED VALUES ARE SUBMITTED TO THE DB
// VALUES THAT MATCH THE ORIGINAL OBJECT ARE STRIPPED BEFORE SUBMITTING
// ALLOWS US TO TRACK THE CHANGED PROPS IN THE SUBMITTED OBJ
compareObjectsKeepChanges: function (originalObj, editedObj){
for(var prop in editedObj) {
if (typeof originalObj[prop] === "object") {
if (this.jsonObj(originalObj[prop]) === this.jsonObj(editedObj[prop])) {
delete editedObj[prop];
}
} else {
if (editedObj[prop] === originalObj[prop]) {
delete editedObj[prop];
}
}
}
if (!this.isObject(editedObj)) {
return;
} else {
return editedObj;
}
},
// VERIFY THAT THERE IS OBJECT
isObject: function (newObj) {
return (Object.keys(newObj).length > 0);
},
// STRINGIFY OBJECT ARRAYS OR OBJECTS WITHIN AN OBJECT
jsonObj: function (obj) {
return JSON.stringify(obj);
},
// REMOVE PROPERTIES FROM OBJECT
stripObj: function (obj, propsToRemove) {
if (obj) {
for (var i = propsToRemove.length; i >= 0; i--) {
if (obj[propsToRemove[i]]) {
delete obj[propsToRemove[i]];
}
}
}
return obj;
},
getUrl: function(url) {
$http.get(url).success(function(obj) {
return obj;
}).error(function(err){
return err;
});
}
};
return utilities;
});
AngularJS Configuration File – app.config.js
‘use strict’;
angular.module(‘interceptcms’)
// USED TEMPORARILY FOR TRACKING STATE CHANGE
// STATE CHANGE SUCCESS ISN’T BEING CALLED
.run(function($rootScope) {
$rootScope.$on(“$stateChangeSuccess”, function(event, toState, toParams, fromState, fromParams) {
if (fromState.name === “”) {
// The initial transition comes from “root”, which uses the empty string as a name.
//console.log(“initial state: ” + toState.name);
} else {
console.log(“NEW state: ” + toState.name);
}
});
})
// NOTE ———————————————–
// THE CREATE STATE REUSES THE EDIT VIEW FOR ALL MODELS
// IF TEMPLATES NEED TO BE SEPARATE CHANGE
// the create objects templateUrl property
// in app.routes.js
.constant(‘RESOURCES’, (function() {
// Define your variable
var HOSTNAME = window.location.hostname;
// Use the variable in your constants
return {
// HOSTNAME
HOSTNAME: HOSTNAME,
// APPLICATON NAME USED IN HEADER
APP_NAME: "Online Publications",
// URLS
// FRONTEND APP
APP_URL: "http://" + HOSTNAME + ":9000/",
// AUTHORIZATION APP
AUTH_URL: "http://" + HOSTNAME + ":3000/",
// DATA FEED
API_URL: "http://" + HOSTNAME + ":1337/",
// NOTIFICATIONS SERVER
SOCKETIO_SERVER: "http://" + HOSTNAME + ":3000/",
// DIRECTORIES
// DEFAULT VIEWS FOLDER
VIEWS: '/views/',
// USED IF FEATURE NESTING IS TRUE
COMPONENTS: "/components/",
// CONTROLLERS
CONTROLLERS: "../controllers/",
// VIEWS EXT
VIEW_EXT: '.html',
// USED IF FEATURE NESTING IS FALSE / AND URLS ARE AUTO GENEREATED
DEFAULTS: "defaults",
// STATE DEFINITIONS / URL PARTIALS / ACTIONS
// CREATE URL AND FILE REFERENCE FOR EACH MODEL
CREATE: "create",
// LIST URL AND FILE REFERENCE FOR EACH MODEL
LIST: "list",
// VIEW URL AND FILE REFERENCE FOR EACH MODEL
VIEW: "view",
// EDIT URL AND FILE REFERENCE FOR EACH MODEL
EDIT: "edit",
// SEARCH STRING DELIMITER
SEARCH: "search",
// A LIST OF FIELDS FOR ALL MODELS THAT ARE FOR INTERNAL / CMS USE ONLY
RECORD_KEEPING_DATA: ['id', 'createdAt', 'createdBy', 'updatedAt', 'updatedBy'],
// A LIST OF FIELDS FOR ALL MODELS THAT ARE FOR INTERNAL / CMS USE ONLY
CTRLS: ['DefaultCtrl', 'TagtypesCtrl', 'TagCtrl'],
// DEFAULT_IMG REFERENCE FOR EACH MODEL
DEFAULT_IMG: "http://placehold.it/400x300",
// ARRAY of Controllers with additional / static / custom routing needs
// CREATE AN ARRAY OF OBJECTS WITH STATE, CTRL and FEATURENESTING PROPERTIES
// ex: [{ state:'essay', ctrl:"EssaysCtrl", featureNesting: false }]
STATECTRLS: [],
LIMIT: 20,
DIR: "desc"
};
})());
/*
// AUTO GENERATED ROUTING
// **** REQUIREMENTS ****
// STATE MUST MATCH
// API MODEL/CLASS NAME
// ex. car
// VIEW MUST BE NAMED
// state-constant VIEW_EXT
// ex. car-edit.html
// MODEL DEFINITION
// MUST BE AVAILABLE FROM
// API_URL/state/model
// ex. localhost:33777/car/model
.constant(‘STATECTRLS’, [
// { state:’essay’, ctrl:”EssaysCtrl”, featureNesting: false },
// { state:’tag’, ctrl:”TagsCtrl”, featureNesting: false },
// { state:’tagType’, ctrl:”TagtypesCtrl”, featureNesting: false }
]
);
*/
Angular JS – UI-Router – Dynamic Routing
‘use strict’;
var templateCache = {};
var controllerCache = {};
angular.module(‘interceptcms’).config(function($urlRouterProvider, $stateProvider, $httpProvider, RESOURCES) {
//$httpProvider.defaults.cache = true;
var url404 = "404";
// PASCAL CASE IS THE PREFERRED ANGULAR NAMING CONVENTION
// FOR CONTROLLERS
function capitalizeFirstletter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function getUrl(url) {
var http = new XMLHttpRequest();
http.open("GET", url, false);
http.send();
if (http.status === 404) {
return;
}
return http.response;
}
// EVERYTHING IS GOING TO a single controller
// ANGULAR DIRECTIVES are being called dynamically via the view
// defaultCtrl.js performs generic data to api actions
function checkControllerCache() {
var model = window.location.hash.split('/')[1] || 'default';
if (model.indexOf('?') > -1) {
model = model.split('?')[0];
}
var modelRef = capitalizeFirstletter(model) + "Ctrl";
var ctrl = getControllerCache(model);
if (ctrl) {
//console.log("returning cached Controller " + ctrl);
} else {
var partial = model + "/" + model + ".js";
if (getUrl(RESOURCES.COMPONENTS + partial) || getUrl(RESOURCES.CONTROLLERS + partial)) {
setControllerCache(model, modelRef);
} else {
setControllerCache(model, 'DefaultCtrl');
ctrl = 'DefaultCtrl';
}
}
return ctrl;
}
function setControllerCache(ctlrRef, ctrl) {
controllerCache[ctlrRef] = ctrl;
}
function getControllerCache(ctlrRef) {
return controllerCache[ctlrRef];
}
// TEMPLATE CACHE
function setTemplateCache(templateName, view) {
templateCache[templateName] = view;
}
function getTemplateCache(templateName) {
return templateCache[templateName];
}
function checkTemplateCache(model, template) {
var tmpl = getTemplateCache(model + template);
var url;
// RETURNS CACHED TEMPLATE
if (tmpl) {
console.log("returning cached view " + model + ":" + template);
} else {
url = RESOURCES.COMPONENTS + model + "/" + template + RESOURCES.VIEW_EXT;
// RETURNS CUSTOM TEMPLATE
tmpl = getUrl(url);
// RETURNS DEFAULT TEMPLATE
if (!tmpl) {
url = RESOURCES.VIEWS + RESOURCES.DEFAULTS + "/" + template + RESOURCES.VIEW_EXT;
tmpl = getUrl(url);
}
// RETURNS 404
if (!tmpl) {
tmpl = getUrl(RESOURCES.VIEWS + url404 + RESOURCES.VIEW_EXT);
}
setTemplateCache(model+template, tmpl);
}
return tmpl;
}
// DEFAULT ROUTE
$urlRouterProvider.otherwise('/');
// EXPLICITLY SET ROUTES
// GENERIC APP FUNCTIONALITY
$stateProvider
.state('main', {
url: '/',
templateUrl: RESOURCES.VIEWS + 'main' + RESOURCES.VIEW_EXT
})
.state('register', {
url: '/register',
templateUrl: RESOURCES.VIEWS + 'register' + RESOURCES.VIEW_EXT,
controller: 'RegisterCtrl'
})
.state('login', {
url: '/login',
templateUrl: RESOURCES.VIEWS + 'login' + RESOURCES.VIEW_EXT,
controller: 'LoginCtrl'
})
.state('forgot', {
url: '/forgot',
templateUrl: RESOURCES.VIEWS + 'forgot' + RESOURCES.VIEW_EXT,
controller: 'ForgotCtrl'
})
.state('reset/:id', {
url: '/reset/:id',
templateUrl: RESOURCES.VIEWS + 'reset' + RESOURCES.VIEW_EXT,
controller: 'ResetCtrl'
})
.state('logout', {
url: '/logout',
controller: 'LogoutCtrl'
});
// DYNAMICALLY ADD STATES FROM ARRAY
// CREATED ON PAGE LOAD
// CREATE AN ARRAY OF OBJECTS WITH STATE, CTRL and FEATURENESTING PROPERTIES
// ex: [{ state:'essay', ctrl:"EssaysCtrl", featureNesting: false }]
function createState(stateCtrl){
var ctrl = stateCtrl.ctrl;
var state = stateCtrl.state;
var path = (stateCtrl.featureNesting) ? RESOURCES.COMPONENTS + "/" + state + "/" : RESOURCES.VIEWS;
var list = {
url: "/" + state ,
templateUrl: path + state + "-" + RESOURCES.LIST + RESOURCES.VIEW_EXT,
controller: ctrl
};
var create = {
url: "/" + state + "/" + RESOURCES.CREATE,
templateUrl: path + state + "-" + RESOURCES.EDIT + RESOURCES.VIEW_EXT,
controller: ctrl
};
var view = {
url: "/" + state + "/:id",
templateUrl: path + state + "-" + RESOURCES.VIEW + RESOURCES.VIEW_EXT,
controller: ctrl
};
var edit = {
url: "/" + state + "/:id" + RESOURCES.EDIT,
templateUrl: path + state + "-" + RESOURCES.EDIT + RESOURCES.VIEW_EXT,
controller: ctrl
};
$stateProvider.state(state, list);
$stateProvider.state(state + "/" + RESOURCES.CREATE, create);
$stateProvider.state(state + "/", view);
$stateProvider.state(state + "/" + RESOURCES.EDIT + "/", edit);
}
// ADD NEW STATES IN FOR EACH LOOP
angular.forEach(RESOURCES.STATECTRLS, function (stateCtrl) {
createState(stateCtrl);
});
// FULL DYNAMIC ROUTING
// CHECKS FOR CUSTOM CONTROLLER NESTED IN COMPONENTS
// CHECKS FOR CUSTOM CONTROLLER IN CONTROLLERS
// FAILS TO GENERIC DEFAULT CONTROLLER
// CHECKS FOR CUSTOM VIEWS NESTED IN COMPONENTS
// FAILS TO GENERIC VIEWS
$stateProvider
// EDIT RECORD
.state('/:model/:id/' + RESOURCES.EDIT, {
url: '/:model/:id/' + RESOURCES.EDIT,
template: function(stateParams) {
return checkTemplateCache(stateParams.model, RESOURCES.EDIT);
},
controller: checkControllerCache()
})
// CREATE RECORD
.state('/:model/' + RESOURCES.CREATE, {
url: '/:model/' + RESOURCES.CREATE,
template: function(stateParams) {
return checkTemplateCache(stateParams.model, RESOURCES.EDIT);
},
controller: checkControllerCache()
})
// LIST VIEW
.state('/:model/' + RESOURCES.LIST, {
url: '/:model/' + RESOURCES.LIST,
template: function(stateParams) {
return checkTemplateCache(stateParams.model, RESOURCES.LIST);
},
controller: checkControllerCache()
})
// RECORD VIEW
.state('/:model/:id', {
url: '/:model/:id',
template: function(stateParams) {
return checkTemplateCache(stateParams.model, RESOURCES.VIEW);
},
controller: checkControllerCache()
})
// SEARCH LIST VIEW
.state('/:model/' + RESOURCES.SEARCH + "/:search?:skip&:limit", {
url: '/:model/' + RESOURCES.SEARCH + "/:search?:skip&:limit",
template: function(stateParams) {
return checkTemplateCache(stateParams.model, RESOURCES.LIST);
},
controller: checkControllerCache()
})
// SEARCH LIST VIEW
.state('/:model/' + RESOURCES.SEARCH + "/:search", {
url: '/:model/' + RESOURCES.SEARCH + "/:search",
template: function(stateParams) {
return checkTemplateCache(stateParams.model, RESOURCES.LIST);
},
controller: checkControllerCache()
})
// ADDITIONAL LIST VIEW
.state('/:model/' + RESOURCES.LIST + '/:page', {
url: '/:model/' + RESOURCES.LIST + '/:page',
template: function(stateParams) {
return checkTemplateCache(stateParams.model, stateParams.page);
},
controller: checkControllerCache()
})
// ADDITIONAL ITEM VIEW
.state('/:model/:id/:page', {
url: '/:model/:id/:page',
template: function(stateParams) {
return checkTemplateCache(stateParams.model, stateParams.page);
},
controller: checkControllerCache()
})
// DEFAULT LIST VIEW
.state('/:model?skip:skip&limit:limit', {
url: '/:model?:skip&:limit',
template: function(stateParams) {
return checkTemplateCache(stateParams.model, RESOURCES.LIST);
},
controller: checkControllerCache()
})
.state('/:model', {
url: '/:model',
template: function(stateParams) {
return checkTemplateCache(stateParams.model, RESOURCES.LIST);
},
controller: checkControllerCache()
});
$httpProvider.interceptors.push('authInterceptor');
});