Commit 493efb4a authored by Oskar Kalbag's avatar Oskar Kalbag

Removed the hybrid mode. Refactored the examples into modules. Moved the...

Removed the hybrid mode. Refactored the examples into modules. Moved the hardcoded formatter out of Tally-Express and into the sample. Changed the namespace for communicating with Tally in the DOM to __tally. Moved the profiled version into its own module and route.
parent d5b16674
// CoffeeScript bootstrap for use with (e.g.) node --debug
require("coffee-script");
require("./index");
...@@ -3,11 +3,6 @@ tally = require './lib/tally-express.coffee' ...@@ -3,11 +3,6 @@ tally = require './lib/tally-express.coffee'
superagent = require 'superagent' superagent = require 'superagent'
timer = require './lib/timer.coffee'
# Time the app startup
timer.reset()
# #
# Set up Express with Tally as the templating engine. # Set up Express with Tally as the templating engine.
# #
...@@ -19,31 +14,7 @@ app.use express.static('views') ...@@ -19,31 +14,7 @@ app.use express.static('views')
# #
# Simple template example (with static data) # Simple template example (with static data)
# #
app.get '/simple', require('./simple.coffee').simple
# Sample data
data =
title: 'Tally sample'
name: 'Tally'
content: 'This is a simple example to demonstrate Tally, a templating engine for Express 3 and client‐side JavaScript built on Distal, a JavaScript implementation of TAL from the Zope framework.'
newURL: 'http://aralbalkan.com'
correctURLFragment: 'moderniosdevelopment'
aralImageURL: 'http://aralbalkan.com/images/aral.jpg'
friends:
[
{name: 'Laura', skills: 'design, development, illustration, speaking'},
{name: 'Seb', skills: 'particles, games, JavaScript, C++'},
{name: 'Natalie', skills: 'HTML, CSS'}
]
# Pure Tally call.
app.get '/', (request, response) ->
data.hybrid = no
response.render 'index.html', data
# Hybrid Tally call.
app.get '/hybrid', (request, response) ->
data.hybrid = yes
response.render 'hybrid.html', data
# #
# App.net global timeline example. # App.net global timeline example.
...@@ -51,31 +22,24 @@ app.get '/hybrid', (request, response) -> ...@@ -51,31 +22,24 @@ app.get '/hybrid', (request, response) ->
app.get '/posts', (request, response) -> app.get '/posts', (request, response) ->
# Time the data call
timer.reset()
superagent.get('https://alpha-api.app.net/stream/0/posts/stream/global') superagent.get('https://alpha-api.app.net/stream/0/posts/stream/global')
.end (globalTimelineResponse) -> .end (globalTimelineResponse) ->
timer.elapsedTime('Data transfer from App.net')
if globalTimelineResponse.body if globalTimelineResponse.body
# Attach a custom function to the data to count the number of posts # Attach a custom function to the data to count the number of posts
globalTimelineResponse.body.numberOfPosts = -> globalTimelineResponse.body.numberOfPosts = ->
return this.data.length return this.data.length
# Time the template render
timer.reset()
response.render 'posts.html', globalTimelineResponse.body response.render 'posts.html', globalTimelineResponse.body
timer.elapsedTime('Template render')
else else
response.render 'posts.html', {error: 'Bad response from Twitter'} response.render 'posts.html', {error: 'Bad response from Twitter'}
# App.net global timeline example with profiling.
app.get '/profile', require('./profile.coffee').profile
app.listen 3000 app.listen 3000
console.log 'The Tally sample is listening on port 3000…' console.log 'The Tally sample is listening on port 3000…'
timer.elapsedTime('Server start')
// Generated by CoffeeScript 1.4.0 // Generated by CoffeeScript 1.4.0
(function() { (function() {
var app, data, express, superagent, tally, timer; var app, express, superagent, tally;
express = require('express'); express = require('express');
...@@ -8,10 +8,6 @@ ...@@ -8,10 +8,6 @@
superagent = require('superagent'); superagent = require('superagent');
timer = require('./lib/timer.coffee');
timer.reset();
app = express(); app = express();
app.engine('html', tally.__express); app.engine('html', tally.__express);
...@@ -20,48 +16,15 @@ ...@@ -20,48 +16,15 @@
app.use(express["static"]('views')); app.use(express["static"]('views'));
data = { app.get('/simple', require('./simple.coffee').simple);
title: 'Tally sample',
name: 'Tally',
content: 'This is a simple example to demonstrate Tally, a templating engine for Express 3 and client‐side JavaScript built on Distal, a JavaScript implementation of TAL from the Zope framework.',
newURL: 'http://aralbalkan.com',
correctURLFragment: 'moderniosdevelopment',
aralImageURL: 'http://aralbalkan.com/images/aral.jpg',
friends: [
{
name: 'Laura',
skills: 'design, development, illustration, speaking'
}, {
name: 'Seb',
skills: 'particles, games, JavaScript, C++'
}, {
name: 'Natalie',
skills: 'HTML, CSS'
}
]
};
app.get('/', function(request, response) {
data.hybrid = false;
return response.render('index.html', data);
});
app.get('/hybrid', function(request, response) {
data.hybrid = true;
return response.render('hybrid.html', data);
});
app.get('/posts', function(request, response) { app.get('/posts', function(request, response) {
timer.reset();
return superagent.get('https://alpha-api.app.net/stream/0/posts/stream/global').end(function(globalTimelineResponse) { return superagent.get('https://alpha-api.app.net/stream/0/posts/stream/global').end(function(globalTimelineResponse) {
timer.elapsedTime('Data transfer from App.net');
if (globalTimelineResponse.body) { if (globalTimelineResponse.body) {
globalTimelineResponse.body.numberOfPosts = function() { globalTimelineResponse.body.numberOfPosts = function() {
return this.data.length; return this.data.length;
}; };
timer.reset(); return response.render('posts.html', globalTimelineResponse.body);
response.render('posts.html', globalTimelineResponse.body);
return timer.elapsedTime('Template render');
} else { } else {
return response.render('posts.html', { return response.render('posts.html', {
error: 'Bad response from Twitter' error: 'Bad response from Twitter'
...@@ -70,10 +33,10 @@ ...@@ -70,10 +33,10 @@
}); });
}); });
app.get('/profile', require('./profile.coffee').profile);
app.listen(3000); app.listen(3000);
console.log('The Tally sample is listening on port 3000…'); console.log('The Tally sample is listening on port 3000…');
timer.elapsedTime('Server start');
}).call(this); }).call(this);
jsdom = require 'jsdom' jsdom = require 'jsdom'
fs = require 'fs' fs = require 'fs'
handlebars = require 'handlebars'
# Load the Tally engine.
tally = (fs.readFileSync __dirname + '/tally.js', 'utf8').toString() tally = (fs.readFileSync __dirname + '/tally.js', 'utf8').toString()
# The Express 3 export.
exports.__express = (path, data, callback) -> exports.__express = (path, data, callback) ->
fs.readFile path, 'utf8', (error, template) -> fs.readFile path, 'utf8', (error, template) ->
if error if error
...@@ -14,20 +15,26 @@ exports.__express = (path, data, callback) -> ...@@ -14,20 +15,26 @@ exports.__express = (path, data, callback) ->
window = document.createWindow() window = document.createWindow()
window.console = console window.console = console
# Flag so Tally knows it is running under Tally-Express # Create a private member in the data to communicate with the Tally engine in the DOM.
# server side so that it knows to remove nodes that data.__tally = {} unless data.__tally
# don’t satisfy conditionals (data-qif) instead of
# setting their display to none as makes sense on the client.
data.aralbalkan = {tallyRunningInNode: yes}
# console.log data # Flag so Tally knows it is running on the server
# and will remove nodes that don’t satisfy conditionals
# (data-qif) instead of setting them to display: none
# like it does when running on the client.
data.__tally['server'] = yes;
window.data = data # Create Tally in the DOM.
window.run tally window.run tally
# Custom formatter # If the user has added custom formatters to the data object,
window.tally.format['fullURL'] = (value) -> # copy them over to Tally.
return 'http://' + value + '.com' customFormatters = data.__tally['formatters']
if customFormatters
window.tally.format[customFormatter] = customFormatters[customFormatter] for customFormatter of customFormatters
# Save the data on the DOM and run Tally.
window.data = data
# NB. window.document is tracing out as [ null ] in the function itself # NB. window.document is tracing out as [ null ] in the function itself
# === although window.document.innerHTML works. window.document.documentElement # === although window.document.innerHTML works. window.document.documentElement
...@@ -36,11 +43,4 @@ exports.__express = (path, data, callback) -> ...@@ -36,11 +43,4 @@ exports.__express = (path, data, callback) ->
html = window.document.innerHTML; html = window.document.innerHTML;
if data.hybrid == yes
console.log 'Hybrid'
# Hybrid: now, let’s run it through handlebars
handleBarsTemplate = handlebars.compile(html)
html = handleBarsTemplate(data)
# console.log(html)
callback(null, html) callback(null, html)
// Generated by CoffeeScript 1.4.0 // Generated by CoffeeScript 1.4.0
(function() { (function(){var e,t,n;t=require("jsdom");e=require("fs");n=e.readFileSync(__dirname+"/tally.js","utf8").toString();exports.__express=function(r,i,s){return e.readFile(r,"utf8",function(e,r){var o,u,a,f,l;if(e)return s(e);a=t.jsdom(r,"2");l=a.createWindow();l.console=console;i.__tally||(i.__tally={});i.__tally.server=!0;l.run(n);u=i.__tally.formatters;if(u)for(o in u)l.tally.format[o]=u[o];l.data=i;l.run("tally(window.document.documentElement, window.data);");f=l.document.innerHTML;return s(null,f)})}}).call(this);
var fs, handlebars, jsdom, tally; \ No newline at end of file
jsdom = require('jsdom');
fs = require('fs');
handlebars = require('handlebars');
tally = (fs.readFileSync(__dirname + '/tally.js', 'utf8')).toString();
exports.__express = function(path, data, callback) {
return fs.readFile(path, 'utf8', function(error, template) {
var document, handleBarsTemplate, html, window;
if (error) {
return callback(error);
}
document = jsdom.jsdom(template, '2');
window = document.createWindow();
window.console = console;
data.aralbalkan = {
tallyRunningInNode: true
};
window.data = data;
window.run(tally);
window.tally.format['fullURL'] = function(value) {
return 'http://' + value + '.com';
};
window.run('tally(window.document.documentElement, window.data);');
html = window.document.innerHTML;
if (data.hybrid === true) {
console.log('Hybrid');
handleBarsTemplate = handlebars.compile(html);
html = handleBarsTemplate(data);
}
return callback(null, html);
});
};
}).call(this);
...@@ -102,8 +102,11 @@ tally = (root, obj) -> ...@@ -102,8 +102,11 @@ tally = (root, obj) ->
list = root.querySelectorAll("*[" + qdup + "]") list = root.querySelectorAll("*[" + qdup + "]")
node.parentNode.removeChild node while (node = list[pos++]) node.parentNode.removeChild node while (node = list[pos++])
pos = 0 pos = 0
listStack = [(if querySelectorAll then root.querySelectorAll(TAL) else root.getElementsByTagName("*"))] listStack = [(if querySelectorAll then root.querySelectorAll(TAL) else root.getElementsByTagName("*"))]
list = [root] list = [root]
loop loop
node = list[pos++] node = list[pos++]
...@@ -112,7 +115,7 @@ tally = (root, obj) -> ...@@ -112,7 +115,7 @@ tally = (root, obj) ->
while not node and (list = listStack.pop()) while not node and (list = listStack.pop())
pos = posStack.pop() pos = posStack.pop()
node = list[pos++] node = list[pos++]
break unless node break unless node
#creates a shortcut to an object #creates a shortcut to an object
#e.g., <section qdef="feeds main.sidebar.feeds"> #e.g., <section qdef="feeds main.sidebar.feeds">
...@@ -175,7 +178,7 @@ tally = (root, obj) -> ...@@ -175,7 +178,7 @@ tally = (root, obj) ->
pos += node.querySelectorAll(TAL).length pos += node.querySelectorAll(TAL).length
else else
pos += node.getElementsByTagName("*").length pos += node.getElementsByTagName("*").length
if obj.aralbalkan isnt `undefined` and obj.aralbalkan.tallyRunningInNode if obj.__tally isnt `undefined` and obj.__tally.server
node.parentNode.removeChild node node.parentNode.removeChild node
else else
node.style.display = "none" node.style.display = "none"
...@@ -188,6 +191,7 @@ tally = (root, obj) -> ...@@ -188,6 +191,7 @@ tally = (root, obj) ->
#of the loop. #of the loop.
#e.g., <div qrepeat="item feeds.items"> #e.g., <div qrepeat="item feeds.items">
attr = node.getAttribute(qrepeat) attr = node.getAttribute(qrepeat)
if attr if attr
attr2 = attr.split(" ") attr2 = attr.split(" ")
...@@ -202,7 +206,7 @@ tally = (root, obj) -> ...@@ -202,7 +206,7 @@ tally = (root, obj) ->
if objList and objList.length if objList and objList.length
# Don’t set the style if on the server (as we don’t on anything) # Don’t set the style if on the server (as we don’t on anything)
node.style.display = "" if obj.aralbalkan is `undefined` node.style.display = "" if obj.__tally is `undefined` or obj.__tally.server is no
#allow this node to be treated as index zero in the repeat list #allow this node to be treated as index zero in the repeat list
#we do this by setting the shortcut variable to array[0] #we do this by setting the shortcut variable to array[0]
...@@ -212,7 +216,7 @@ tally = (root, obj) -> ...@@ -212,7 +216,7 @@ tally = (root, obj) ->
# Handling hiding differently depending on whether this is running on the # Handling hiding differently depending on whether this is running on the
# client or as part of Tally on the server. # client or as part of Tally on the server.
if obj.aralbalkan isnt `undefined` and obj.aralbalkan.tallyRunningInNode if obj.__tally isnt `undefined` and obj.__tally.server
# Running in Tally as part of Express. # Running in Tally as part of Express.
# Delete the node # Delete the node
......
...@@ -170,7 +170,7 @@ Forked from Distal by mocking@gmail.com (https://code.google.com/p/distal/) ...@@ -170,7 +170,7 @@ Forked from Distal by mocking@gmail.com (https://code.google.com/p/distal/)
} else { } else {
pos += node.getElementsByTagName("*").length; pos += node.getElementsByTagName("*").length;
} }
if (obj.aralbalkan !== undefined && obj.aralbalkan.tallyRunningInNode) { if (obj.__tally !== undefined && obj.__tally.server) {
node.parentNode.removeChild(node); node.parentNode.removeChild(node);
} else { } else {
node.style.display = "none"; node.style.display = "none";
...@@ -192,13 +192,13 @@ Forked from Distal by mocking@gmail.com (https://code.google.com/p/distal/) ...@@ -192,13 +192,13 @@ Forked from Distal by mocking@gmail.com (https://code.google.com/p/distal/)
} }
objList = resolve(obj, attr2[1]); objList = resolve(obj, attr2[1]);
if (objList && objList.length) { if (objList && objList.length) {
if (obj.aralbalkan === undefined) { if (obj.__tally === undefined || obj.__tally.server === false) {
node.style.display = ""; node.style.display = "";
} }
obj[attr2[0]] = objList[0]; obj[attr2[0]] = objList[0];
obj["#"] = 1; obj["#"] = 1;
} else { } else {
if (obj.aralbalkan !== undefined && obj.aralbalkan.tallyRunningInNode) { if (obj.__tally !== undefined && obj.__tally.server) {
if (querySelectorAll) { if (querySelectorAll) {
pos += node.querySelectorAll(TAL).length; pos += node.querySelectorAll(TAL).length;
} else { } else {
......
../coffee-script/bin/cake
\ No newline at end of file
../coffee-script/bin/coffee
\ No newline at end of file
*.coffee
*.html
.DS_Store
.git*
Cakefile
documentation/
examples/
extras/coffee-script.js
raw/
src/
test/
coffeescript.org
\ No newline at end of file
## How to contribute to CoffeeScript
* Before you open a ticket or send a pull request, [search](https://github.com/jashkenas/coffee-script/issues) for previous discussions about the same feature or issue. Add to the earlier ticket if you find one.
* Before sending a pull request for a feature, be sure to have [tests](https://github.com/jashkenas/coffee-script/tree/master/test).
* Use the same coding style as the rest of the [codebase](https://github.com/jashkenas/coffee-script/tree/master/src). If you're just getting started with CoffeeScript, there's a nice [style guide](https://github.com/polarmobile/coffeescript-style-guide).
* In your pull request, do not add documentation to `index.html` or re-build the minified `coffee-script.js` file. We'll do those things before cutting a new release.
\ No newline at end of file
Copyright (c) 2009-2012 Jeremy Ashkenas
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
{
} } {
{ { } }
} }{ {
{ }{ } } _____ __ __
( }{ }{ { ) / ____| / _|/ _|
.- { { } { }} -. | | ___ | |_| |_ ___ ___
( ( } { } { } } ) | | / _ \| _| _/ _ \/ _ \
|`-..________ ..-'| | |___| (_) | | | || __/ __/
| | \_____\___/|_| |_| \___|\___|
| ;--.
| (__ \ _____ _ _
| | ) ) / ____| (_) | |
| |/ / | (___ ___ _ __ _ _ __ | |_
| ( / \___ \ / __| '__| | '_ \| __|
| |/ ____) | (__| | | | |_) | |_
| | |_____/ \___|_| |_| .__/ \__|
`-.._________..-' | |
|_|
CoffeeScript is a little language that compiles into JavaScript.
Install Node.js, and then the CoffeeScript compiler:
sudo bin/cake install
Or, if you have the Node Package Manager installed:
npm install -g coffee-script
(Leave off the -g if you don't wish to install globally.)
Execute a script:
coffee /path/to/script.coffee
Compile a script:
coffee -c /path/to/script.coffee
For documentation, usage, and examples, see:
http://coffeescript.org/
To suggest a feature, report a bug, or general discussion:
http://github.com/jashkenas/coffee-script/issues/
If you'd like to chat, drop by #coffeescript on Freenode IRC,
or on webchat.freenode.net.
The source repository:
git://github.com/jashkenas/coffee-script.git
All contributors are listed here:
http://github.com/jashkenas/coffee-script/contributors
require 'rubygems'
require 'erb'
require 'fileutils'
require 'rake/testtask'
require 'json'
desc "Build the documentation page"
task :doc do
source = 'documentation/index.html.erb'
child = fork { exec "bin/coffee -bcw -o documentation/js documentation/coffee/*.coffee" }
at_exit { Process.kill("INT", child) }
Signal.trap("INT") { exit }
loop do
mtime = File.stat(source).mtime
if !@mtime || mtime > @mtime
rendered = ERB.new(File.read(source)).result(binding)
File.open('index.html', 'w+') {|f| f.write(rendered) }
end
@mtime = mtime
sleep 1
end
end
desc "Build coffee-script-source gem"
task :gem do
require 'rubygems'
require 'rubygems/package'
gemspec = Gem::Specification.new do |s|
s.name = 'coffee-script-source'
s.version = JSON.parse(File.read('package.json'))["version"]
s.date = Time.now.strftime("%Y-%m-%d")
s.homepage = "http://jashkenas.github.com/coffee-script/"
s.summary = "The CoffeeScript Compiler"
s.description = <<-EOS
CoffeeScript is a little language that compiles into JavaScript.
Underneath all of those embarrassing braces and semicolons,
JavaScript has always had a gorgeous object model at its heart.
CoffeeScript is an attempt to expose the good parts of JavaScript
in a simple way.
EOS
s.files = [
'lib/coffee_script/coffee-script.js',
'lib/coffee_script/source.rb'
]
s.authors = ['Jeremy Ashkenas']
s.email = 'jashkenas@gmail.com'
s.rubyforge_project = 'coffee-script-source'
s.license = "MIT"
end
file = File.open("coffee-script-source.gem", "w")
Gem::Package.open(file, 'w') do |pkg|
pkg.metadata = gemspec.to_yaml
path = "lib/coffee_script/source.rb"
contents = <<-ERUBY
module CoffeeScript
module Source
def self.bundled_path
File.expand_path("../coffee-script.js", __FILE__)
end
end
end