├── .gitattributes
├── .gitignore
├── app
├── templates
│ ├── _generator.json
│ ├── README.md.erb
│ ├── gitignore
│ ├── bowerrc
│ ├── public
│ │ ├── css
│ │ │ └── app.css
│ │ ├── js
│ │ │ ├── home
│ │ │ │ └── _home-controller.js
│ │ │ └── _app.js
│ │ ├── views
│ │ │ └── home
│ │ │ │ └── _home.html
│ │ └── _index.html
│ ├── editorconfig
│ ├── jshintrc
│ ├── _bower.json
│ ├── models
│ │ └── _index.js
│ ├── _package.json
│ ├── _app.js
│ └── Gruntfile.js
└── index.js
├── entity
├── templates
│ ├── _generator.json
│ ├── public
│ │ ├── js
│ │ │ └── entity
│ │ │ │ ├── _entity-service.js
│ │ │ │ ├── _entity-router.js
│ │ │ │ └── _entity-controller.js
│ │ └── views
│ │ │ └── entity
│ │ │ └── _entities.html
│ ├── models
│ │ └── _entity.js
│ └── routes
│ │ └── _entities.js
└── index.js
├── .editorconfig
├── .travis.yml
├── test
├── test-load.js
└── test-creation.js
├── .jshintrc
├── LICENSE
├── package.json
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | temp/
3 | npm-debug.log
4 |
--------------------------------------------------------------------------------
/app/templates/_generator.json:
--------------------------------------------------------------------------------
1 | <%= generatorConfigStr %>
2 |
--------------------------------------------------------------------------------
/entity/templates/_generator.json:
--------------------------------------------------------------------------------
1 | <%= generatorConfigStr %>
2 |
--------------------------------------------------------------------------------
/app/templates/README.md.erb:
--------------------------------------------------------------------------------
1 | ## Overview
2 |
3 | Blah blah
4 |
5 |
--------------------------------------------------------------------------------
/app/templates/gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.swp
3 | *~
4 | node_modules
5 |
--------------------------------------------------------------------------------
/app/templates/bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "public/lib",
3 | "json": "bower.json"
4 | }
5 |
--------------------------------------------------------------------------------
/app/templates/public/css/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin-top: 60px;
3 | padding: 10px;
4 | background-color: #ECF0F1;
5 | }
6 |
--------------------------------------------------------------------------------
/app/templates/public/js/home/_home-controller.js:
--------------------------------------------------------------------------------
1 | angular.module('<%= baseName %>')
2 | .controller('HomeController', ['$scope', function ($scope) {
3 | }]);
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/app/templates/editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | - '0.8'
5 | before_install:
6 | - currentfolder=${PWD##*/}
7 | - if [ "$currentfolder" != 'generator-angular-express-sequelize' ]; then cd .. && eval "mv $currentfolder generator-angular-express-sequelize" && cd generator-angular-express-sequelize; fi
8 |
9 |
--------------------------------------------------------------------------------
/test/test-load.js:
--------------------------------------------------------------------------------
1 | /*global describe, beforeEach, it*/
2 | 'use strict';
3 |
4 | var assert = require('assert');
5 |
6 | describe('angular-express-sequelize generator', function () {
7 | it('can be imported without blowing up', function () {
8 | var app = require('../app');
9 | assert(app !== undefined);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/entity/templates/public/js/entity/_entity-service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= baseName %>')
4 | .factory('<%= _.capitalize(name) %>', ['$resource', function ($resource) {
5 | return $resource('<%= baseName %>/<%= pluralize(name) %>/:id', {}, {
6 | 'query': { method: 'GET', isArray: true},
7 | 'get': { method: 'GET'},
8 | 'update': { method: 'PUT'}
9 | });
10 | }]);
11 |
--------------------------------------------------------------------------------
/app/templates/public/js/_app.js:
--------------------------------------------------------------------------------
1 | // Declare app level module which depends on filters, and services
2 | angular.module('<%= baseName %>', ['ngResource', 'ngRoute', 'ui.bootstrap', 'ui.date'])
3 | .config(['$routeProvider', function ($routeProvider) {
4 | $routeProvider
5 | .when('/', {
6 | templateUrl: 'views/home/home.html',
7 | controller: 'HomeController'})
8 | .otherwise({redirectTo: '/'});
9 | }]);
10 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "camelcase": true,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "indent": 4,
10 | "latedef": true,
11 | "newcap": true,
12 | "noarg": true,
13 | "quotmark": "single",
14 | "regexp": true,
15 | "undef": true,
16 | "unused": true,
17 | "strict": true,
18 | "trailing": true,
19 | "smarttabs": true,
20 | "white": true
21 | }
22 |
--------------------------------------------------------------------------------
/app/templates/jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "camelcase": true,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "indent": 4,
10 | "latedef": true,
11 | "newcap": true,
12 | "noarg": true,
13 | "quotmark": "single",
14 | "regexp": true,
15 | "undef": true,
16 | "unused": true,
17 | "strict": true,
18 | "trailing": true,
19 | "smarttabs": true,
20 | "white": true
21 | }
22 |
--------------------------------------------------------------------------------
/app/templates/_bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= _.camelize(baseName) %>",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular-bootstrap": "~0.10.0",
6 | "angular-resource": "~1.2.13",
7 | "angular-route": "~1.2.13",
8 | "angular-ui-date": "~0.0.3",
9 | "angular": "~1.2.13",
10 | "bootstrap-css": "~3.0.0",
11 | "jquery": "~2.1.0",
12 | "lodash": "~2.4.1"
13 | },
14 | "devDependencies": {
15 | "angular-mocks": "~1.2.13",
16 | "angular-scenario": "~1.2.13"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/templates/public/views/home/_home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Welcome to <%= _.capitalize(baseName) %>!
5 |
This is your homepage, ready for editing
6 |
7 |
8 | If you like this Angular-Express-Sequelize generator, please give us a star at Github!
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/entity/templates/public/js/entity/_entity-router.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= baseName %>')
4 | .config(['$routeProvider', function ($routeProvider) {
5 | $routeProvider
6 | .when('/<%= pluralize(name) %>', {
7 | templateUrl: 'views/<%= name %>/<%= pluralize(name) %>.html',
8 | controller: '<%= _.capitalize(name) %>Controller',
9 | resolve:{
10 | resolved<%= _.capitalize(name) %>: ['<%= _.capitalize(name) %>', function (<%= _.capitalize(name) %>) {
11 | return <%= _.capitalize(name) %>.query();
12 | }]
13 | }
14 | })
15 | }]);
16 |
--------------------------------------------------------------------------------
/app/templates/models/_index.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs')
2 | , path = require('path')
3 | , Sequelize = require('sequelize')
4 | , lodash = require('lodash')
5 | , sequelize = new Sequelize('sequelize_test', 'root', null, {
6 | dialect: "sqlite", // or 'sqlite', 'postgres', 'mariadb'
7 | storage: "/tmp/my.db",
8 | })
9 | , db = {}
10 |
11 | fs
12 | .readdirSync(__dirname)
13 | .filter(function(file) {
14 | return ((file.indexOf('.') !== 0) && (file !== 'index.js') && (file.slice(-3) == '.js'))
15 | })
16 | .forEach(function(file) {
17 | var model = sequelize.import(path.join(__dirname, file))
18 | db[model.name] = model
19 | })
20 |
21 | Object.keys(db).forEach(function(modelName) {
22 | if (db[modelName].hasOwnProperty('associate')) {
23 | db[modelName].associate(db)
24 | }
25 | })
26 |
27 | module.exports = lodash.extend({
28 | sequelize: sequelize,
29 | Sequelize: Sequelize
30 | }, db)
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2013 Robert Yokota
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/entity/templates/models/_entity.js:
--------------------------------------------------------------------------------
1 | module.exports = function(sequelize, DataTypes) {
2 | var <%= _.capitalize(name) %> = sequelize.define('<%= _.capitalize(name) %>', {
3 | <% _.each(attrs, function (attr) { %>
4 | <%= attr.attrName %>: {
5 | type: DataTypes.<%= attr.attrType.toUpperCase() %><% if (attr.attrType == 'Enum') { %>(<% var delim = ''; _.each(attr.enumValues, function (value) { %><%= delim %>'<%= value %>'<% delim = ', '; }) %>)<% }; %>,
6 | validate: {
7 | notNull: <%= attr.required %>,
8 | <% if (attr.maxLength) { if (attr.minLength) { %>len: [<%= attr.minLength %>, <%= attr.maxLength %>],<% } else { %>len: [0, <%= attr.maxLength %>],<% } };%>
9 | <% if (attr.min) { %>min: <%= attr.min %>,<% };%>
10 | <% if (attr.max) { %>max: <%= attr.max %>,<% };%>
11 | },
12 | <% if (attr.attrType == 'Date') { %>get: function() {
13 | var value = this.getDataValue('<%= attr.attrName %>')
14 | return value ? value.toISOString().substring(0, 10) : value
15 | }<% }; %>
16 | },
17 | <% }); %>
18 | })
19 |
20 | return <%= _.capitalize(name) %>
21 | }
22 |
--------------------------------------------------------------------------------
/test/test-creation.js:
--------------------------------------------------------------------------------
1 | /*global describe, beforeEach, it*/
2 | 'use strict';
3 |
4 | var path = require('path');
5 | var helpers = require('yeoman-generator').test;
6 |
7 |
8 | describe('angular-express-sequelize generator', function () {
9 | beforeEach(function (done) {
10 | helpers.testDirectory(path.join(__dirname, 'temp'), function (err) {
11 | if (err) {
12 | return done(err);
13 | }
14 |
15 | this.app = helpers.createGenerator('angular-express-sequelize:app', [
16 | '../../app'
17 | ]);
18 | done();
19 | }.bind(this));
20 | });
21 |
22 | it('creates expected files', function (done) {
23 | var expected = [
24 | // add files you expect to exist here.
25 | '.jshintrc',
26 | '.editorconfig'
27 | ];
28 |
29 | helpers.mockPrompt(this.app, {
30 | 'someOption': true
31 | });
32 | this.app.options['skip-install'] = true;
33 | this.app.run({}, function () {
34 | helpers.assertFiles(expected);
35 | done();
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-angular-express-sequelize",
3 | "version": "0.1.13",
4 | "description": "A Yeoman generator for AngularJS + Express + Sequelize",
5 | "keywords": [
6 | "yeoman-generator",
7 | "Node.js",
8 | "Express",
9 | "Sequelize",
10 | "AngularJS",
11 | "Bootstrap"
12 | ],
13 | "homepage": "https://github.com/rayokota/generator-angular-express-sequelize",
14 | "bugs": "https://github.com/rayokota/generator-angular-express-sequelize/issues",
15 | "author": {
16 | "name": "Robert Yokota",
17 | "email": "",
18 | "url": "https://github.com/rayokota"
19 | },
20 | "main": "app/index.js",
21 | "repository": {
22 | "type": "git",
23 | "url": "git://github.com/rayokota/generator-angular-express-sequelize.git"
24 | },
25 | "scripts": {
26 | "test": "mocha"
27 | },
28 | "dependencies": {
29 | "yeoman-generator": "~0.14.0",
30 | "URIjs": "~1.11.2",
31 | "lodash": "~2.4.1",
32 | "underscore.string": "~2.3.3",
33 | "pluralize": "~0.0.6",
34 | "asciify": "~1.3.3"
35 | },
36 | "devDependencies": {
37 | "mocha": "~1.14.0"
38 | },
39 | "peerDependencies": {
40 | "yo": ">=1.0.0"
41 | },
42 | "engines": {
43 | "node": ">=0.8.0",
44 | "npm": ">=1.2.10"
45 | },
46 | "licenses": [
47 | {
48 | "type": "MIT"
49 | }
50 | ]
51 | }
52 |
--------------------------------------------------------------------------------
/entity/templates/routes/_entities.js:
--------------------------------------------------------------------------------
1 | var db = require('../models')
2 |
3 | exports.findAll = function(req, res) {
4 | db.<%= _.capitalize(name) %>.findAll().success(function(entities) {
5 | res.json(entities)
6 | })
7 | }
8 |
9 | exports.find = function(req, res) {
10 | db.<%= _.capitalize(name) %>.find({ where: { id: req.param('id') } }).success(function(entity) {
11 | if (entity) {
12 | res.json(entity)
13 | } else {
14 | res.send(404)
15 | }
16 | })
17 | }
18 |
19 | exports.create = function(req, res) {
20 | db.<%= _.capitalize(name) %>.create(req.body).success(function(entity) {
21 | res.statusCode = 201
22 | res.json(entity)
23 | })
24 | }
25 |
26 | exports.update = function(req, res) {
27 | db.<%= _.capitalize(name) %>.find({ where: { id: req.param('id') } }).success(function(entity) {
28 | if (entity) {
29 | entity.updateAttributes(req.body).success(function(entity) {
30 | res.json(entity)
31 | })
32 | } else {
33 | res.send(404)
34 | }
35 | })
36 | }
37 |
38 | exports.destroy = function(req, res) {
39 | db.<%= _.capitalize(name) %>.find({ where: { id: req.param('id') } }).success(function(entity) {
40 | if (entity) {
41 | entity.destroy().success(function() {
42 | res.send(204)
43 | })
44 | } else {
45 | res.send(404)
46 | }
47 | })
48 | }
49 |
--------------------------------------------------------------------------------
/app/templates/_package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= _.slugify(baseName) %>",
3 | "version": "0.0.0",
4 | "description": "Description for <%= baseName %>",
5 | "scripts": {
6 | "start": "node app.js"
7 | },
8 | "dependencies": {
9 | "express": "4.1.1",
10 | "body-parser": "~1.0.0",
11 | "errorhandler": "~1.0.0",
12 | "method-override": "~1.0.0",
13 | "morgan": "~1.0.0",
14 | "sequelize": "~2.0.0-beta.2",
15 | "sqlite3": "~2.1.5",
16 | "lodash": "~2.4.1"
17 | },
18 | "devDependencies": {
19 | "grunt": "~0.4.2",
20 | "grunt-autoprefixer": "~0.4.2",
21 | "grunt-bowercopy": "~0.4.1",
22 | "grunt-bower-install": "~0.7.0",
23 | "grunt-concurrent": "~0.4.2",
24 | "grunt-connect-proxy": "~0.2.0",
25 | "grunt-contrib-clean": "~0.5.0",
26 | "grunt-contrib-concat": "~0.3.0",
27 | "grunt-contrib-connect": "~0.5.0",
28 | "grunt-contrib-copy": "~0.4.1",
29 | "grunt-contrib-cssmin": "~0.7.0",
30 | "grunt-contrib-htmlmin": "~0.1.3",
31 | "grunt-contrib-imagemin": "~0.4.0",
32 | "grunt-contrib-jshint": "~0.7.2",
33 | "grunt-contrib-uglify": "~0.2.7",
34 | "grunt-contrib-watch": "~0.5.3",
35 | "grunt-karma": "~0.6.2",
36 | "grunt-modernizr": "~0.4.1",
37 | "grunt-ngmin": "~0.0.3",
38 | "grunt-rev": "~0.1.0",
39 | "grunt-svgmin": "~0.3.0",
40 | "grunt-sync": "~0.0.5",
41 | "grunt-usemin": "~2.0.2",
42 | "load-grunt-tasks": "~0.2.0",
43 | "time-grunt": "0.2.3",
44 | "karma" : "~0.10.8",
45 | "karma-junit-reporter" : "~0.1.0",
46 | "karma-jasmine" : "~0.1.0",
47 | "karma-ng-scenario" : "~0.1.0"
48 | },
49 | "engines": {
50 | "node": ">=0.8.15"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/templates/_app.js:
--------------------------------------------------------------------------------
1 | var express = require('express')
2 | , bodyParser = require('body-parser')
3 | , errorHandler = require('errorhandler')
4 | , methodOverride = require('method-override')
5 | , morgan = require('morgan')
6 | , http = require('http')
7 | , path = require('path')
8 | , db = require('./models')
9 | <% _.each(entities, function (entity) { %>
10 | , <%= pluralize(entity.name) %> = require('./routes/<%= pluralize(entity.name) %>')<% }); %>
11 |
12 | var app = express()
13 |
14 | // all environments
15 | app.set('port', process.env.PORT || 3000)
16 | app.set('views', __dirname + '/views')
17 | app.set('view engine', 'jade')
18 | app.use(morgan('dev'))
19 | app.use(bodyParser())
20 | app.use(methodOverride())
21 | app.use(express.static(path.join(__dirname, 'public')))
22 |
23 | // development only
24 | if ('development' === app.get('env')) {
25 | app.use(errorHandler())
26 | }
27 |
28 | <% _.each(entities, function (entity) { %>
29 | app.get('/<%= baseName %>/<%= pluralize(entity.name) %>', <%= pluralize(entity.name) %>.findAll)
30 | app.get('/<%= baseName %>/<%= pluralize(entity.name) %>/:id', <%= pluralize(entity.name) %>.find)
31 | app.post('/<%= baseName %>/<%= pluralize(entity.name) %>', <%= pluralize(entity.name) %>.create)
32 | app.put('/<%= baseName %>/<%= pluralize(entity.name) %>/:id', <%= pluralize(entity.name) %>.update)
33 | app.del('/<%= baseName %>/<%= pluralize(entity.name) %>/:id', <%= pluralize(entity.name) %>.destroy)
34 | <% }); %>
35 |
36 | db
37 | .sequelize
38 | .sync()
39 | .complete(function(err) {
40 | if (err) {
41 | throw err
42 | } else {
43 | http.createServer(app).listen(app.get('port'), function() {
44 | console.log('Express server listening on port ' + app.get('port'))
45 | })
46 | }
47 | })
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The Angular-Express-Sequelize generator
2 |
3 | A [Yeoman](http://yeoman.io) generator for [AngularJS](http://angularjs.org) and [Express](http://expressjs.com) with [Sequelize](http://sequelizejs.com).
4 |
5 | Express is a Javascript-based micro-framework. For AngularJS integration with other micro-frameworks, see https://github.com/rayokota/MicroFrameworkRosettaStone.
6 |
7 | ## Installation
8 |
9 | Install [Git](http://git-scm.com) and [node.js](http://nodejs.org). The development mode also requires [SQLite](http://www.sqlite.org).
10 |
11 | Install Yeoman:
12 |
13 | npm install -g yo
14 |
15 | Install the Angular-Express-Sequelize generator:
16 |
17 | npm install -g generator-angular-express-sequelize
18 |
19 | The above prerequisites can be installed to a VM using the [Angular-Express-Sequelize provisioner](https://github.com/rayokota/provision-angular-express-sequelize).
20 |
21 | ## Creating an Express service
22 |
23 | In a new directory, generate the service:
24 |
25 | yo angular-express-sequelize
26 |
27 | Run the service:
28 |
29 | node app.js
30 |
31 | Your service will run at [http://localhost:3000](http://localhost:3000).
32 |
33 |
34 | ## Creating a persistent entity
35 |
36 | Generate the entity:
37 |
38 | yo angular-express-sequelize:entity [myentity]
39 |
40 | You will be asked to specify attributes for the entity, where each attribute has the following:
41 |
42 | - a name
43 | - a type (String, Integer, Float, Boolean, Date, Enum)
44 | - for a String attribute, an optional minimum and maximum length
45 | - for a numeric attribute, an optional minimum and maximum value
46 | - for a Date attribute, an optional constraint to either past values or future values
47 | - for an Enum attribute, a list of enumerated values
48 | - whether the attribute is required
49 |
50 | Files that are regenerated will appear as conflicts. Allow the generator to overwrite these files as long as no custom changes have been made.
51 |
52 | Run the service:
53 |
54 | node app.js
55 |
56 | A client-side AngularJS application will now be available by running
57 |
58 | grunt server
59 |
60 | The Grunt server will run at [http://localhost:9000](http://localhost:9000). It will proxy REST requests to the Express service running at [http://localhost:3000](http://localhost:3000).
61 |
62 | At this point you should be able to navigate to a page to manage your persistent entities.
63 |
64 | The Grunt server supports hot reloading of client-side HTML/CSS/Javascript file changes.
65 |
66 |
--------------------------------------------------------------------------------
/app/templates/public/_index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= _.capitalize(baseName) %>
8 |
9 |
10 |
11 |
12 |
13 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | <% _.each(entities, function (entity) { %>
52 |
53 |
54 |
55 | <% }); %>
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/templates/Gruntfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest;
4 |
5 | module.exports = function (grunt) {
6 | require('load-grunt-tasks')(grunt);
7 | require('time-grunt')(grunt);
8 |
9 | grunt.initConfig({
10 | yeoman: {
11 | // configurable paths
12 | app: require('./bower.json').appPath || 'public',
13 | dist: 'public'
14 | },
15 | sync: {
16 | dist: {
17 | files: [{
18 | cwd: '<%%= yeoman.app %>',
19 | dest: '<%%= yeoman.dist %>',
20 | src: '**'
21 | }]
22 | }
23 | },
24 | watch: {
25 | options: {
26 | livereload: 35729
27 | },
28 | src: {
29 | files: [
30 | '<%%= yeoman.app %>/*.html',
31 | '<%%= yeoman.app %>/css/**/*',
32 | '<%%= yeoman.app %>/js/**/*',
33 | '<%%= yeoman.app %>/views/**/*'
34 | ],
35 | //tasks: ['sync:dist']
36 | }
37 | },
38 | connect: {
39 | proxies: [
40 | {
41 | context: '/<%= baseName %>',
42 | host: 'localhost',
43 | port: 3000,
44 | https: false,
45 | changeOrigin: false
46 | }
47 | ],
48 | options: {
49 | port: 9000,
50 | // Change this to '0.0.0.0' to access the server from outside.
51 | hostname: 'localhost',
52 | livereload: 35729
53 | },
54 | livereload: {
55 | options: {
56 | open: true,
57 | base: [
58 | '<%%= yeoman.app %>'
59 | ],
60 | middleware: function (connect) {
61 | return [
62 | proxySnippet,
63 | connect.static(require('path').resolve('public'))
64 | ];
65 | }
66 | }
67 | },
68 | /*
69 | dist: {
70 | options: {
71 | base: '<%%= yeoman.dist %>'
72 | }
73 | }
74 | */
75 | },
76 | // Put files not handled in other tasks here
77 | copy: {
78 | dist: {
79 | files: [{
80 | expand: true,
81 | dot: true,
82 | cwd: '<%%= yeoman.app %>',
83 | dest: '<%%= yeoman.dist %>',
84 | src: '**'
85 | }]
86 | },
87 | },
88 | // Test settings
89 | karma: {
90 | unit: {
91 | configFile: 'test/config/karma.conf.js',
92 | singleRun: true
93 | }
94 | },
95 | bowercopy: {
96 | options: {
97 | destPrefix: '<%%= yeoman.app %>'
98 | },
99 | test: {
100 | files: {
101 | 'test/lib/angular-mocks': 'angular-mocks',
102 | 'test/lib/angular-scenario': 'angular-scenario'
103 | }
104 | }
105 | }
106 | });
107 |
108 | grunt.registerTask('server', function (target) {
109 | grunt.task.run([
110 | //'copy:dist',
111 | 'configureProxies',
112 | 'connect:livereload',
113 | 'watch'
114 | ]);
115 | });
116 | };
117 |
--------------------------------------------------------------------------------
/entity/templates/public/js/entity/_entity-controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= baseName %>')
4 | .controller('<%= _.capitalize(name) %>Controller', ['$scope', '$modal', 'resolved<%= _.capitalize(name) %>', '<%= _.capitalize(name) %>',
5 | function ($scope, $modal, resolved<%= _.capitalize(name) %>, <%= _.capitalize(name) %>) {
6 |
7 | $scope.<%= pluralize(name) %> = resolved<%= _.capitalize(name) %>;
8 |
9 | $scope.create = function () {
10 | $scope.clear();
11 | $scope.open();
12 | };
13 |
14 | $scope.update = function (id) {
15 | $scope.<%= name %> = <%= _.capitalize(name) %>.get({id: id});
16 | $scope.open(id);
17 | };
18 |
19 | $scope.delete = function (id) {
20 | <%= _.capitalize(name) %>.delete({id: id},
21 | function () {
22 | $scope.<%= pluralize(name) %> = <%= _.capitalize(name) %>.query();
23 | });
24 | };
25 |
26 | $scope.save = function (id) {
27 | if (id) {
28 | <%= _.capitalize(name) %>.update({id: id}, $scope.<%= name %>,
29 | function () {
30 | $scope.<%= pluralize(name) %> = <%= _.capitalize(name) %>.query();
31 | $scope.clear();
32 | });
33 | } else {
34 | <%= _.capitalize(name) %>.save($scope.<%= name %>,
35 | function () {
36 | $scope.<%= pluralize(name) %> = <%= _.capitalize(name) %>.query();
37 | $scope.clear();
38 | });
39 | }
40 | };
41 |
42 | $scope.clear = function () {
43 | $scope.<%= name %> = {
44 | <% _.each(attrs, function (attr) { %>
45 | "<%= attr.attrName %>": "",
46 | <% }); %>
47 | "id": ""
48 | };
49 | };
50 |
51 | $scope.open = function (id) {
52 | var <%= name %>Save = $modal.open({
53 | templateUrl: '<%= name %>-save.html',
54 | controller: '<%= _.capitalize(name) %>SaveController',
55 | resolve: {
56 | <%= name %>: function () {
57 | return $scope.<%= name %>;
58 | }
59 | }
60 | });
61 |
62 | <%= name %>Save.result.then(function (entity) {
63 | $scope.<%= name %> = entity;
64 | $scope.save(id);
65 | });
66 | };
67 | }])
68 | .controller('<%= _.capitalize(name) %>SaveController', ['$scope', '$modalInstance', '<%= name %>',
69 | function ($scope, $modalInstance, <%= name %>) {
70 | $scope.<%= name %> = <%= name %>;
71 |
72 | <% _.each(attrs, function (attr) { if (attr.attrType === 'Date') { %>
73 | $scope.<%= attr.attrName %>DateOptions = {
74 | dateFormat: 'yy-mm-dd',
75 | <% if (attr.dateConstraint === 'Past') { %>maxDate: -1<% } %>
76 | <% if (attr.dateConstraint === 'Future') { %>minDate: 1<% } %>
77 | };<% }}); %>
78 |
79 | $scope.ok = function () {
80 | $modalInstance.close($scope.<%= name %>);
81 | };
82 |
83 | $scope.cancel = function () {
84 | $modalInstance.dismiss('cancel');
85 | };
86 | }]);
87 |
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var util = require('util'),
3 | path = require('path'),
4 | yeoman = require('yeoman-generator'),
5 | _ = require('lodash'),
6 | _s = require('underscore.string'),
7 | pluralize = require('pluralize'),
8 | asciify = require('asciify');
9 |
10 | var AngularExpressSequelizeGenerator = module.exports = function AngularExpressSequelizeGenerator(args, options, config) {
11 | yeoman.generators.Base.apply(this, arguments);
12 |
13 | this.on('end', function () {
14 | this.installDependencies({ skipInstall: options['skip-install'] });
15 | });
16 |
17 | this.pkg = JSON.parse(this.readFileAsString(path.join(__dirname, '../package.json')));
18 | };
19 |
20 | util.inherits(AngularExpressSequelizeGenerator, yeoman.generators.Base);
21 |
22 | AngularExpressSequelizeGenerator.prototype.askFor = function askFor() {
23 |
24 | var cb = this.async();
25 |
26 | console.log('\n' +
27 | '+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+\n' +
28 | '|a|n|g|u|l|a|r| |e|x|p|r|e|s|s| |s|e|q|u|e|l|i|z|e| |g|e|n|e|r|a|t|o|r|\n' +
29 | '+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+\n' +
30 | '\n');
31 |
32 | var prompts = [{
33 | type: 'input',
34 | name: 'baseName',
35 | message: 'What is the name of your application?',
36 | default: 'myapp'
37 | }];
38 |
39 | this.prompt(prompts, function (props) {
40 | this.baseName = props.baseName;
41 |
42 | cb();
43 | }.bind(this));
44 | };
45 |
46 | AngularExpressSequelizeGenerator.prototype.app = function app() {
47 |
48 | this.entities = [];
49 | this.resources = [];
50 | this.generatorConfig = {
51 | "baseName": this.baseName,
52 | "entities": this.entities,
53 | "resources": this.resources
54 | };
55 | this.generatorConfigStr = JSON.stringify(this.generatorConfig, null, '\t');
56 |
57 | this.template('_generator.json', 'generator.json');
58 | this.template('_package.json', 'package.json');
59 | this.template('_bower.json', 'bower.json');
60 | this.template('bowerrc', '.bowerrc');
61 | this.template('Gruntfile.js', 'Gruntfile.js');
62 | this.copy('gitignore', '.gitignore');
63 |
64 | var modelsDir = 'models/'
65 | var publicDir = 'public/'
66 | var routesDir = 'routes/'
67 | var viewsDir = 'views/'
68 | this.mkdir(modelsDir);
69 | this.mkdir(publicDir);
70 | this.mkdir(routesDir);
71 | this.mkdir(viewsDir);
72 |
73 | this.template('_app.js', 'app.js');
74 | this.template('models/_index.js', modelsDir + 'index.js');
75 |
76 | var publicCssDir = publicDir + 'css/';
77 | var publicJsDir = publicDir + 'js/';
78 | var publicViewDir = publicDir + 'views/';
79 | this.mkdir(publicCssDir);
80 | this.mkdir(publicJsDir);
81 | this.mkdir(publicViewDir);
82 | this.template('public/_index.html', publicDir + 'index.html');
83 | this.copy('public/css/app.css', publicCssDir + 'app.css');
84 | this.template('public/js/_app.js', publicJsDir + 'app.js');
85 | this.template('public/js/home/_home-controller.js', publicJsDir + 'home/home-controller.js');
86 | this.template('public/views/home/_home.html', publicViewDir + 'home/home.html');
87 | };
88 |
89 | AngularExpressSequelizeGenerator.prototype.projectfiles = function projectfiles() {
90 | this.copy('editorconfig', '.editorconfig');
91 | this.copy('jshintrc', '.jshintrc');
92 | };
93 |
--------------------------------------------------------------------------------
/entity/templates/public/views/entity/_entities.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
<%= _.capitalize(pluralize(name)) %>
4 |
5 |
64 |
65 |
68 |
69 |
70 |
71 |
72 |
73 | | ID |
74 | <% _.each(attrs, function (attr) { %>
75 | <%= attr.attrName %> |
76 | <% }); %>
77 |
78 |
79 |
80 |
81 | | {{<%= name %>.id}} |
82 | <% _.each(attrs, function (attr) { %>
83 | {{<%= name %>.<%= attr.attrName %> <% if (attr.attrType === 'Date') { %> | date:'yyyy-MM-dd'<% } %>}} |
84 | <% }); %>
85 |
86 |
91 |
96 | |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/entity/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var util = require('util'),
3 | yeoman = require('yeoman-generator'),
4 | fs = require('fs'),
5 | _ = require('lodash'),
6 | _s = require('underscore.string'),
7 | pluralize = require('pluralize');
8 |
9 | var EntityGenerator = module.exports = function EntityGenerator(args, options, config) {
10 | // By calling `NamedBase` here, we get the argument to the subgenerator call
11 | // as `this.name`.
12 | yeoman.generators.NamedBase.apply(this, arguments);
13 |
14 | console.log('You called the entity subgenerator with the argument ' + this.name + '.');
15 |
16 | fs.readFile('generator.json', 'utf8', function (err, data) {
17 | if (err) {
18 | console.log('Error: ' + err);
19 | return;
20 | }
21 | this.generatorConfig = JSON.parse(data);
22 | }.bind(this));
23 | };
24 |
25 | util.inherits(EntityGenerator, yeoman.generators.NamedBase);
26 |
27 | EntityGenerator.prototype.askFor = function askFor() {
28 | var cb = this.async();
29 |
30 | console.log('\nPlease specify an attribute:');
31 |
32 | var prompts = [{
33 | type: 'input',
34 | name: 'attrName',
35 | message: 'What is the name of the attribute?',
36 | default: 'myattr'
37 | },
38 | {
39 | type: 'list',
40 | name: 'attrType',
41 | message: 'What is the type of the attribute?',
42 | choices: ['String', 'Integer', 'Float', 'Boolean', 'Date', 'Enum'],
43 | default: 'String'
44 | },
45 | {
46 | when: function (props) { return (/String/).test(props.attrType); },
47 | type: 'input',
48 | name: 'minLength',
49 | message: 'Enter the minimum length for the String attribute, or hit enter:',
50 | validate: function (input) {
51 | if (input && isNaN(input)) {
52 | return "Please enter a number.";
53 | }
54 | return true;
55 | }
56 | },
57 | {
58 | when: function (props) { return (/String/).test(props.attrType); },
59 | type: 'input',
60 | name: 'maxLength',
61 | message: 'Enter the maximum length for the String attribute, or hit enter:',
62 | validate: function (input) {
63 | if (input && isNaN(input)) {
64 | return "Please enter a number.";
65 | }
66 | return true;
67 | }
68 | },
69 | {
70 | when: function (props) { return (/Integer|Float/).test(props.attrType); },
71 | type: 'input',
72 | name: 'min',
73 | message: 'Enter the minimum value for the numeric attribute, or hit enter:',
74 | validate: function (input) {
75 | if (input && isNaN(input)) {
76 | return "Please enter a number.";
77 | }
78 | return true;
79 | }
80 | },
81 | {
82 | when: function (props) { return (/Integer|Float/).test(props.attrType); },
83 | type: 'input',
84 | name: 'max',
85 | message: 'Enter the maximum value for the numeric attribute, or hit enter:',
86 | validate: function (input) {
87 | if (input && isNaN(input)) {
88 | return "Please enter a number.";
89 | }
90 | return true;
91 | }
92 | },
93 | {
94 | when: function (props) { return (/Date/).test(props.attrType); },
95 | type: 'list',
96 | name: 'dateConstraint',
97 | message: 'Constrain the date as follows:',
98 | choices: ['None', 'Past dates only', 'Future dates only'],
99 | filter: function (input) {
100 | if (/Past/.test(input)) return 'Past';
101 | if (/Future/.test(input)) return 'Future';
102 | return '';
103 | },
104 | default: 'None'
105 | },
106 | {
107 | when: function (props) { return (/Enum/).test(props.attrType); },
108 | type: 'input',
109 | name: 'enumValues',
110 | message: 'Enter an enumeration of values, separated by commas'
111 | },
112 | {
113 | type: 'confirm',
114 | name: 'required',
115 | message: 'Is the attribute required to have a value?',
116 | default: true
117 | },
118 | {
119 | type: 'confirm',
120 | name: 'again',
121 | message: 'Would you like to enter another attribute or reenter a previous attribute?',
122 | default: true
123 | }];
124 |
125 | this.prompt(prompts, function (props) {
126 | this.attrs = this.attrs || [];
127 | var attrType = props.attrType;
128 | this.attrs = _.reject(this.attrs, function (attr) { return attr.attrName === props.attrName; });
129 | this.attrs.push({
130 | attrName: props.attrName,
131 | attrType: attrType,
132 | minLength: props.minLength,
133 | maxLength: props.maxLength,
134 | min: props.min,
135 | max: props.max,
136 | dateConstraint: props.dateConstraint,
137 | enumValues: props.enumValues ? props.enumValues.split(',') : [],
138 | required: props.required
139 | });
140 |
141 | if (props.again) {
142 | this.askFor();
143 | } else {
144 | cb();
145 | }
146 | }.bind(this));
147 | };
148 |
149 | EntityGenerator.prototype.files = function files() {
150 |
151 | this.baseName = this.generatorConfig.baseName;
152 | this.packageName = this.generatorConfig.packageName;
153 | this.entities = this.generatorConfig.entities;
154 | this.entities = _.reject(this.entities, function (entity) { return entity.name === this.name; }.bind(this));
155 | this.entities.push({ name: this.name, attrs: this.attrs});
156 | this.pluralize = pluralize;
157 | this.generatorConfig.entities = this.entities;
158 | this.generatorConfigStr = JSON.stringify(this.generatorConfig, null, '\t');
159 |
160 | this.template('_generator.json', 'generator.json');
161 | this.template('../../app/templates/_app.js', 'app.js');
162 | this.template('models/_entity.js', 'models/' + this.name + '.js');
163 | this.template('routes/_entities.js', 'routes/' + pluralize(this.name) + '.js');
164 |
165 | var publicDir = 'public/';
166 | var publicCssDir = publicDir + 'css/';
167 | var publicJsDir = publicDir + 'js/';
168 | var publicViewDir = publicDir + 'views/';
169 | var publicEntityJsDir = publicJsDir + this.name + '/';
170 | var publicEntityViewDir = publicViewDir + this.name + '/';
171 | this.mkdir(publicEntityJsDir);
172 | this.mkdir(publicEntityViewDir);
173 | this.template('../../app/templates/public/_index.html', publicDir + 'index.html');
174 | this.template('public/js/entity/_entity-controller.js', publicEntityJsDir + this.name + '-controller.js');
175 | this.template('public/js/entity/_entity-router.js', publicEntityJsDir + this.name + '-router.js');
176 | this.template('public/js/entity/_entity-service.js', publicEntityJsDir + this.name + '-service.js');
177 | this.template('public/views/entity/_entities.html', publicEntityViewDir + pluralize(this.name) + '.html');
178 | };
179 |
--------------------------------------------------------------------------------