Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

route context example #22

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions 07_route_context/Gruntfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
var path = require('path');

var stylesheetsDir = 'assets/stylesheets';
var rendrDir = 'node_modules/rendr';
var rendrHandlebarsDir = 'node_modules/rendr-handlebars';
var rendrModulesDir = rendrDir + '/node_modules';

module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),

stylus: {
compile: {
options: {
paths: [stylesheetsDir],
'include css': true
},
files: {
'public/styles.css': stylesheetsDir + '/index.styl'
}
}
},

handlebars: {
compile: {
options: {
namespace: false,
commonjs: true,
processName: function(filename) {
return filename.replace('app/templates/', '').replace('.hbs', '');
}
},
src: "app/templates/**/*.hbs",
dest: "app/templates/compiledTemplates.js",
filter: function(filepath) {
var filename = path.basename(filepath);
// Exclude files that begin with '__' from being sent to the client,
// i.e. __layout.hbs.
return filename.slice(0, 2) !== '__';
}
}
},

watch: {
scripts: {
files: 'app/**/*.js',
tasks: ['browserify'],
options: {
interrupt: true
}
},
templates: {
files: 'app/**/*.hbs',
tasks: ['handlebars'],
options: {
interrupt: true
}
},
stylesheets: {
files: [stylesheetsDir + '/**/*.styl', stylesheetsDir + '/**/*.css'],
tasks: ['stylus'],
options: {
interrupt: true
}
}
},

browserify: {
basic: {
src: [
'app/**/*.js',
],
dest: 'public/mergedAssets.js',
options: {
debug: true,
alias: [
'node_modules/handlebars/lib/index.js:handlebars',
'node_modules/rendr-handlebars/index.js:rendr-handlebars',
],
aliasMappings: [
{
cwd: 'app/',
src: ['**/*.js'],
dest: 'app/'
},
],
shim: {
jquery: {
path: 'assets/vendor/jquery-1.9.1.min.js',
exports: '$',
},
},
}
}
}
});

grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-handlebars');
grunt.loadNpmTasks('grunt-contrib-stylus');
grunt.loadNpmTasks('grunt-contrib-watch');

grunt.registerTask('runNode', function () {
grunt.util.spawn({
cmd: 'node',
args: ['./node_modules/nodemon/nodemon.js', 'index.js'],
opts: {
stdio: 'inherit'
}
}, function () {
grunt.fail.fatal(new Error("nodemon quit"));
});
});


grunt.registerTask('compile', ['handlebars', 'browserify', 'stylus']);

// Run the server and watch for file changes
grunt.registerTask('server', ['compile', 'runNode', 'watch']);

// Default task(s).
grunt.registerTask('default', ['compile']);

};

71 changes: 71 additions & 0 deletions 07_route_context/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Rendr App Template
## GitHub Browser

The purpose of this little app is to demonstrate one way of using Rendr to build a web app that runs on both the client and the server.

![Screenshot](http://cl.ly/image/062d3S2D1Y38/Screen%20Shot%202013-04-09%20at%203.14.31%20PM.png)

## Running the example

First, make sure to have Node >= 0.8.0 [installed on your system](http://nodejs.org/). Also, make sure to have `grunt-cli` installed globally.

$ npm install -g grunt-cli

If you see an error on startup that looks [like this](https://github.com/rendrjs/rendr-app-template/issues/2), then you may need to un-install a global copy of `grunt`:

$ npm uninstall -g grunt

Run `npm install` to install dependencies:

$ npm install

Then, use `grunt server` to start up the web server. Grunt will recompile and restart the server when files change.

$ grunt server

Running "runNode" task

Running "handlebars:compile" (handlebars) task
11 Dec 17:40:30 - [nodemon] v0.7.10
11 Dec 17:40:30 - [nodemon] to restart at any time, enter `rs`
11 Dec 17:40:30 - [nodemon] watching: /Users/spike/code/rendr/examples/00_simple
File "app/templates/compiledTemplates.js" created.

Running "browserify:basic" (browserify) task
11 Dec 17:40:30 - [nodemon] starting `node index.js`
connect.multipart() will be removed in connect 3.0
visit https://github.com/senchalabs/connect/wiki/Connect-3.0 for alternatives
connect.limit() will be removed in connect 3.0
server pid 86724 listening on port 3030 in development mode
>> Bundled public/mergedAssets.js

Running "stylus:compile" (stylus) task
File public/styles.css created.

Running "watch" task
Waiting...

11 Dec 17:40:32 - [nodemon] starting `node index.js`
server pid 86728 listening on port 3030 in development mode

Now, pull up the app in your web browser. It defaults to port `3030`.

$ open http://localhost:3030

You can choose a different port by passing the `PORT` environment variable:

$ PORT=80 grunt server

### GitHub API rate limit

GitHub [rate limits](http://developer.github.com/v3/#rate-limiting) unauthenticated requests to its public API to 60 requests per hour per IP. This should be enough for just playing with the sample app, but if you pull it down and start developing off it you may run up against the rate limit.

**You've been warned.** Your best bet may be to alter the project to read from your favorite RESTful API.

## Overview

At times, it's useful to have certain contextual data checked on each route request (traditional page request) to determine access to that page or to determine what to show on that page. This example shows one way of accomplishing this in Rendr.

## License

MIT
91 changes: 91 additions & 0 deletions 07_route_context/app/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
var BaseApp = require('rendr/shared/app'),
Session = require('./models/session'),
handlebarsHelpers = require('./lib/handlebarsHelpers');

/**
* Extend the `BaseApp` class, adding any custom methods or overrides.
*/
module.exports = BaseApp.extend({

/**
* Client and server.
*
* `initialize` is called on app initialize, both on the client and server.
* On the server, an app is instantiated once for each request, and in the
* client, it's instantiated once on page load.
*
* This is a good place to initialize any code that needs to be available to
* app on both client and server.
*/
initialize: function() {
/**
* Register our Handlebars helpers.
*
* `this.templateAdapter` is, by default, the `rendr-handlebars` module.
* It has a `registerHelpers` method, which allows us to register helper
* modules that can be used on both client & server.
*/
this.templateAdapter.registerHelpers(handlebarsHelpers);
},

/**
* Client-side only.
*
* `start` is called at the bottom of `__layout.hbs`. Calling this kicks off
* the router and initializes the application.
*
* Override this method (remembering to call the superclass' `start` method!)
* in order to do things like bind events to the router, as shown below.
*/
start: function() {
// Show a loading indicator when the app is fetching.
this.router.on('action:start', function() { this.set({loading: true}); }, this);
this.router.on('action:end', function() { this.set({loading: false}); }, this);

// Call 'super'.
BaseApp.prototype.start.call(this);
},

getSessionModel: function() {
var cachedSessionModel = this.fetcher.modelStore.get('Session', 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be a bad practice to directly access the modelStore not sure what anyone elses thoughts are around that though.

if (cachedSessionModel) {
return cachedSessionModel;
} else {
// Then, let's pull the bootstrapped version from the middleware
var session = this.get('session');
session = session || {};
session.id = 1; // make the id 1 for now.
// Then, we convert it to model before we set it.
var sessionModel = new Session(session, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the ideal case is to use https://github.com/expressjs/session and do sessions in middleware instead. Maybe we can change this example to use those things?

app: this
});
return sessionModel;
}
},

refreshSessionModel: function(callback) {
// if this is server side, just run callback
if (this.req) {
callback();
// if this is client side, fetch fresh session model first
} else {
this.getSessionModel().fetch({
success: function(model) {
model.store();

if (callback) {
callback();
}
}
});
}
},

getTimeLoaded: function() {
return this.getSessionModel().get('time');
},

getMinuteLoaded: function() {
return this.getSessionModel().get('minute');
}
});
3 changes: 3 additions & 0 deletions 07_route_context/app/collections/base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var RendrBase = require('rendr/shared/base/collection');

module.exports = RendrBase.extend({});
15 changes: 15 additions & 0 deletions 07_route_context/app/controllers/home_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
var ContextUtils = require('../lib/contextUtils');

module.exports = {
index: ContextUtils.loadBaseData(function(params, callback) {
callback(null, params);
}),

only_even_minute: ContextUtils.ensureOnlyEvenMinute(function(params, callback) {
callback(null, params);
}),

only_odd_minute: ContextUtils.ensureOnlyOddMinute(function(params, callback) {
callback(null, params);
})
};
55 changes: 55 additions & 0 deletions 07_route_context/app/lib/contextUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
var ContextUtils = function() {};

ContextUtils = {

loadBaseData: function(actionHandler) {
return function() {
var _arguments = arguments;
this.app.refreshSessionModel(function() {
actionHandler.apply(this, _arguments);
}.bind(this));
};
},

/**
* An example to show how to limit access based on data stored in our
* app. In this case we only allow access on odd minutes.
* but it could be when we have a user token.
*/
ensureOnlyOddMinute: function(actionHandler) {
return function() {
var _arguments = arguments;

this.app.refreshSessionModel(function() {
var minute = this.app.getMinuteLoaded();
if ((minute % 2) === 1) {
actionHandler.apply(this, _arguments);
} else {
this.redirectTo('/?message=odd+page+is+not+available');
}
}.bind(this));
};
},

/**
* An example to show how to limit access based on data stored in our
* app. In this case we only allow access on even minutes.
* but it could be when we have a user token.
*/
ensureOnlyEvenMinute: function(actionHandler) {
return function() {
var _arguments = arguments;

this.app.refreshSessionModel(function() {
var minute = this.app.getMinuteLoaded();
if ((minute % 2) === 0) {
actionHandler.apply(this, _arguments);
} else {
this.redirectTo('/?message=even+page+is+not+available');
}
}.bind(this));
};
}
};

module.exports = ContextUtils;
11 changes: 11 additions & 0 deletions 07_route_context/app/lib/handlebarsHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* We inject the Handlebars instance, because this module doesn't know where
* the actual Handlebars instance will come from.
*/
module.exports = function(Handlebars) {
return {
copyright: function(year) {
return new Handlebars.SafeString("©" + year);
}
};
};
3 changes: 3 additions & 0 deletions 07_route_context/app/models/base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var RendrBase = require('rendr/shared/base/model');

module.exports = RendrBase.extend({});
7 changes: 7 additions & 0 deletions 07_route_context/app/models/session.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var Base = require('./base');

module.exports = Base.extend({
url: '/sessions/:id',
api: 'local'
});
module.exports.id = 'Session';
Loading