JackDanger-jack 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +16 -0
- data/README.markdown +29 -0
- data/Rakefile +18 -0
- data/bin/jack +19 -0
- data/jack.gemspec +46 -0
- data/lib/jack.js +40 -0
- data/lib/jack.rb +25 -0
- data/lib/jack/action.js +22 -0
- data/lib/jack/baconl.js +254 -0
- data/lib/jack/utils.js +27 -0
- data/lib/jack/view.js +21 -0
- data/samples/framework/boot.js +34 -0
- data/samples/framework/templates/index.html.haml +6 -0
- data/test/app.js +32 -0
- data/test/test_jack.rb +48 -0
- metadata +121 -0
data/Manifest.txt
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Manifest.txt
|
2
|
+
README.markdown
|
3
|
+
Rakefile
|
4
|
+
bin/jack
|
5
|
+
jack.gemspec
|
6
|
+
lib/jack.js
|
7
|
+
lib/jack.rb
|
8
|
+
lib/jack/action.js
|
9
|
+
lib/jack/baconl.js
|
10
|
+
lib/jack/utils.js
|
11
|
+
lib/jack/view.js
|
12
|
+
samples/framework/boot.js
|
13
|
+
samples/framework/templates/index.html.haml
|
14
|
+
test/app.js
|
15
|
+
test/test.log
|
16
|
+
test/test_jack.rb
|
data/README.markdown
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Jack
|
2
|
+
|
3
|
+
Rack + Javascript = Jack
|
4
|
+
|
5
|
+
Write your web apps in the same powerful language on both ends.
|
6
|
+
|
7
|
+
## Does it work?
|
8
|
+
|
9
|
+
Yes, thanks entirely to the hard work of [Christian Neukirchen](http://chneukirchen.org/) for Rack and John Barnette, Aaron
|
10
|
+
Patterson, Yehuda Katz, and Matthew Draper for their work on [Johnson](http://github.com/jbarnette/johnson).
|
11
|
+
|
12
|
+
## Why would I need this?
|
13
|
+
|
14
|
+
Because this is kickass, that's why:
|
15
|
+
|
16
|
+
function(env){
|
17
|
+
return [200, {'Content-Type': 'text/html'}, "you're looking at: "+env['PATH_INFO']];
|
18
|
+
};
|
19
|
+
|
20
|
+
## How do I try it painlessly?
|
21
|
+
|
22
|
+
$ sudo gem install jbarnette-johnson --source=http://gems.github.com
|
23
|
+
$ git clone git://github.com/JackDanger/jack.git # or one of the forks
|
24
|
+
$ cd jack && rake install_gem
|
25
|
+
[...]
|
26
|
+
$ samples/hello.js &
|
27
|
+
$ curl http://localhost:1337
|
28
|
+
|
29
|
+
First version (and idea) by Jack Danger Canty, this version by Phil Hagelberg.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require File.dirname(__FILE__) + '/lib/jack'
|
6
|
+
|
7
|
+
Hoe.new('jack', Jack::VERSION) do |p|
|
8
|
+
p.summary = "A web framework for Javascript."
|
9
|
+
p.developer('Jack Danger Canty', 'gems@6brand.com')
|
10
|
+
p.developer('Phil Hagelberg', 'technomancy@gmail.com')
|
11
|
+
|
12
|
+
p.extra_deps << ['jbarnette-johnson', '~> 1.0.0']
|
13
|
+
p.extra_deps << ['rack', '~> 0.9.0']
|
14
|
+
p.extra_deps << ['clip', '~> 1.0.0']
|
15
|
+
p.extra_dev_deps << ['minitest', '~> 1.3.0']
|
16
|
+
end
|
17
|
+
|
18
|
+
# vim: syntax=Ruby
|
data/bin/jack
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'clip'
|
5
|
+
require File.dirname(__FILE__) + '/../lib/jack'
|
6
|
+
|
7
|
+
clip = Clip do |p|
|
8
|
+
p.optional 'o', 'host', :desc => 'listen on HOST', :default => '127.0.0.1'
|
9
|
+
p.optional('p', 'port', :desc => 'use PORT', :default => 1337) { |v| v.to_i }
|
10
|
+
p.optional('l', 'log', :desc => 'log to file LOG', :default => STDOUT)
|
11
|
+
end
|
12
|
+
|
13
|
+
if clip.valid? and clip.remainder.size == 1
|
14
|
+
Jack.run(clip.remainder.first, clip)
|
15
|
+
else
|
16
|
+
abort "Usage: jack app.js\n#{clip.to_s}"
|
17
|
+
end
|
18
|
+
|
19
|
+
|
data/jack.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = %q{jack}
|
4
|
+
s.version = "0.2.0"
|
5
|
+
|
6
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
7
|
+
s.authors = ["Jack Danger Canty", "Phil Hagelberg"]
|
8
|
+
s.date = %q{2009-03-19}
|
9
|
+
s.default_executable = %q{jack}
|
10
|
+
s.email = ["gems@6brand.com", "technomancy@gmail.com"]
|
11
|
+
s.executables = ["jack"]
|
12
|
+
s.extra_rdoc_files = ["Manifest.txt"]
|
13
|
+
s.files = ["Manifest.txt", "README.markdown", "Rakefile", "bin/jack", "jack.gemspec", "lib/jack.js", "lib/jack.rb", "lib/jack/action.js", "lib/jack/baconl.js", "lib/jack/utils.js", "lib/jack/view.js", "samples/framework/boot.js", "samples/framework/templates/index.html.haml", "test/app.js", "test/test.log", "test/test_jack.rb"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.rdoc_options = ["--main", "README.txt"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{jack}
|
18
|
+
s.rubygems_version = %q{1.3.1}
|
19
|
+
s.summary = %q{A web framework for Javascript.}
|
20
|
+
s.test_files = ["test/test_jack.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 2
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<jbarnette-johnson>, ["~> 1.0.0"])
|
28
|
+
s.add_runtime_dependency(%q<rack>, ["~> 0.9.0"])
|
29
|
+
s.add_runtime_dependency(%q<clip>, ["~> 1.0.0"])
|
30
|
+
s.add_development_dependency(%q<minitest>, ["~> 1.3.0"])
|
31
|
+
s.add_development_dependency(%q<hoe>, [">= 1.11.0"])
|
32
|
+
else
|
33
|
+
s.add_dependency(%q<jbarnette-johnson>, ["~> 1.0.0"])
|
34
|
+
s.add_dependency(%q<rack>, ["~> 0.9.0"])
|
35
|
+
s.add_dependency(%q<clip>, ["~> 1.0.0"])
|
36
|
+
s.add_dependency(%q<minitest>, ["~> 1.3.0"])
|
37
|
+
s.add_dependency(%q<hoe>, [">= 1.11.0"])
|
38
|
+
end
|
39
|
+
else
|
40
|
+
s.add_dependency(%q<jbarnette-johnson>, ["~> 1.0.0"])
|
41
|
+
s.add_dependency(%q<rack>, ["~> 0.9.0"])
|
42
|
+
s.add_dependency(%q<clip>, ["~> 1.0.0"])
|
43
|
+
s.add_dependency(%q<minitest>, ["~> 1.3.0"])
|
44
|
+
s.add_dependency(%q<hoe>, [">= 1.11.0"])
|
45
|
+
end
|
46
|
+
end
|
data/lib/jack.js
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
Johnson.require("johnson/browser");
|
2
|
+
Johnson.require("johnson/browser/jquery");
|
3
|
+
|
4
|
+
var Jack = {
|
5
|
+
version: Ruby.Jack.VERSION,
|
6
|
+
default_headers: {'Content-Type': 'text/html'},
|
7
|
+
|
8
|
+
up: function(){
|
9
|
+
Ruby.Rack.Handler.Mongrel.run(
|
10
|
+
function(env) {
|
11
|
+
Ruby.Dir.glob("controllers/*.js").each(function(file) {
|
12
|
+
eval(Ruby.File.read(file));
|
13
|
+
});
|
14
|
+
|
15
|
+
try {
|
16
|
+
return Jack.Action.route(
|
17
|
+
env,
|
18
|
+
Jack.Utils.parameterize(env['QUERY_STRING'])
|
19
|
+
);
|
20
|
+
}
|
21
|
+
catch(e) {
|
22
|
+
return Jack.application_error(e);
|
23
|
+
}
|
24
|
+
},
|
25
|
+
Jack.options || {}
|
26
|
+
)
|
27
|
+
},
|
28
|
+
|
29
|
+
not_found: function () {
|
30
|
+
return [404, Jack.default_headers, "<h1>Not Found</h1>"];
|
31
|
+
},
|
32
|
+
|
33
|
+
application_error: function (e) {
|
34
|
+
var info = e.inspect ? e.inspect() : e.name + ": " + e.message
|
35
|
+
Jack.log.error(info);
|
36
|
+
var html = ["<h1>Application Error: " + info + "</h1>",
|
37
|
+
"<p>" + e.toString() + "</p>"].join("\n");
|
38
|
+
return [500, Jack.default_headers, html];
|
39
|
+
}
|
40
|
+
};
|
data/lib/jack.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'johnson'
|
4
|
+
require 'rack'
|
5
|
+
gem 'builder'
|
6
|
+
|
7
|
+
module Jack
|
8
|
+
VERSION = '0.2.0'
|
9
|
+
|
10
|
+
def self.run(app_file, options)
|
11
|
+
@js = Johnson::Runtime.new
|
12
|
+
@js.load(File.dirname(__FILE__) + "/jack.js")
|
13
|
+
Dir.glob(File.dirname(__FILE__) + "/jack/*.js").each { |f| @js.load(f) }
|
14
|
+
|
15
|
+
@log = Logger.new(options.log)
|
16
|
+
@log.info "Starting #{app_file} on http://#{options.host}:#{options.port}"
|
17
|
+
@js['Jack']['root'] = File.dirname(app_file)
|
18
|
+
@js['Jack']['log'] = @log # Why can't we go the other way? (Jack.log = Ruby.Jack.log)
|
19
|
+
@js['Jack']['options'] = {:Port => options.port, :Host => options.host}
|
20
|
+
|
21
|
+
@js.load(app_file)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.log; @log end
|
25
|
+
end
|
data/lib/jack/action.js
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Jack.Action = (function() {
|
2
|
+
var routes = [];
|
3
|
+
var actionFactory = function(route, callback) {
|
4
|
+
routes.push({
|
5
|
+
route: route,
|
6
|
+
action: callback
|
7
|
+
});
|
8
|
+
};
|
9
|
+
|
10
|
+
actionFactory.route = function(env, params) {
|
11
|
+
var callback = Jack.Utils.detect(routes, function(i, action) {
|
12
|
+
return action.route.test(env['PATH_INFO']);
|
13
|
+
});
|
14
|
+
var result = !callback ?
|
15
|
+
Jack.not_found() :
|
16
|
+
callback.action.call({}, env, params)
|
17
|
+
return (Array == result.constructor && 3 == result.length) ? result : [
|
18
|
+
200, Jack.default_headers , result
|
19
|
+
];
|
20
|
+
}
|
21
|
+
return actionFactory;
|
22
|
+
})();
|
data/lib/jack/baconl.js
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
var baconl = (function() {
|
2
|
+
var self = function( template ) {
|
3
|
+
var tree;
|
4
|
+
if ( template.isNode !== true ) {
|
5
|
+
tree = baconl.baconize( template );
|
6
|
+
}
|
7
|
+
else {
|
8
|
+
tree = template;
|
9
|
+
}
|
10
|
+
return tree.html();
|
11
|
+
}
|
12
|
+
|
13
|
+
return self
|
14
|
+
})();
|
15
|
+
|
16
|
+
baconl.map = function ( array , callb ) {
|
17
|
+
var result = [];
|
18
|
+
for (var i=0; i < array.length; i++) {
|
19
|
+
result.push( callb( i , array[i] ) );
|
20
|
+
};
|
21
|
+
return result;
|
22
|
+
}
|
23
|
+
|
24
|
+
baconl.each = function( array , callb ) {
|
25
|
+
for (var i=0; i < array.length; i++) {
|
26
|
+
callb( i , array[i] );
|
27
|
+
};
|
28
|
+
return array;
|
29
|
+
}
|
30
|
+
|
31
|
+
baconl.grep = function( array , callb ) {
|
32
|
+
var result = [];
|
33
|
+
baconl.each( array , function(i,obj) {
|
34
|
+
if ( callb( i , obj ) ) {
|
35
|
+
result.push( obj );
|
36
|
+
}
|
37
|
+
} );
|
38
|
+
return result;
|
39
|
+
}
|
40
|
+
|
41
|
+
baconl.parse = function( template ) {
|
42
|
+
if ( template === undefined ) { return undefined; }
|
43
|
+
var tokens = {
|
44
|
+
id: undefined,
|
45
|
+
classes:[],
|
46
|
+
tag: undefined,
|
47
|
+
innerHTML: ""
|
48
|
+
};
|
49
|
+
|
50
|
+
var tagDefinition = /^\s*%([A-Za-z][A-Za-z0-9]*)/;
|
51
|
+
var idDefinition = /^\#([A-Za-z][A-Za-z0-9:_\-]*)/;
|
52
|
+
var classDefinition = /^\.([A-Za-z][A-Za-z0-9:_\-]*)/;
|
53
|
+
|
54
|
+
// TODO: parse JSON and use it for the element attributes
|
55
|
+
// var jsonDefinition =
|
56
|
+
|
57
|
+
var innerHTMLDefinition = /\s*\\?((.|[\n])+)/m;
|
58
|
+
|
59
|
+
// Retrieve HTML tag
|
60
|
+
var nextToken = template.match( tagDefinition );
|
61
|
+
if ( !!nextToken ) {
|
62
|
+
tokens.tag = nextToken[1];
|
63
|
+
}
|
64
|
+
|
65
|
+
|
66
|
+
template = template.replace( tagDefinition , "" );
|
67
|
+
|
68
|
+
// Retrieve element ID
|
69
|
+
nextToken = template.match( idDefinition );
|
70
|
+
tokens.id = !!nextToken ? nextToken[1] : undefined;
|
71
|
+
|
72
|
+
template = template.replace( idDefinition , "" );
|
73
|
+
|
74
|
+
// Retrieve classes
|
75
|
+
nextToken = template.match( classDefinition );
|
76
|
+
|
77
|
+
while( !!nextToken ) {
|
78
|
+
tokens.classes.push( nextToken[1] );
|
79
|
+
template = template.replace( classDefinition , "" );
|
80
|
+
nextToken = template.match( classDefinition );
|
81
|
+
}
|
82
|
+
|
83
|
+
if ( tokens.tag === undefined && ( tokens.id !== undefined || tokens.classes.length > 0 ) ) {
|
84
|
+
tokens.tag = "div";
|
85
|
+
}
|
86
|
+
|
87
|
+
// Retrieve inner content
|
88
|
+
nextToken = template.match( innerHTMLDefinition );
|
89
|
+
tokens.innerHTML = !!nextToken ? nextToken[1] : undefined;
|
90
|
+
|
91
|
+
return tokens;
|
92
|
+
|
93
|
+
}
|
94
|
+
|
95
|
+
baconl.node = function( definition ) {
|
96
|
+
if ( definition !== undefined && definition.isNode ) {
|
97
|
+
return definition;
|
98
|
+
}
|
99
|
+
var self = {
|
100
|
+
definition: definition||"" ,
|
101
|
+
isNode: true,
|
102
|
+
depth: 0 ,
|
103
|
+
childNodes: [] ,
|
104
|
+
parentNode: undefined,
|
105
|
+
parent: function() { return self.parentNode; }
|
106
|
+
};
|
107
|
+
|
108
|
+
function htmlBody() {
|
109
|
+
var node = self;
|
110
|
+
var result = "";
|
111
|
+
|
112
|
+
function openTag( tokens ) {
|
113
|
+
if ( tokens.tag === undefined ) { return ""; }
|
114
|
+
var result = "<" + tokens.tag;
|
115
|
+
if ( tokens.id !== undefined ) { result += " id='" + tokens.id + "'"; }
|
116
|
+
if ( tokens.classes.length > 0 ) {
|
117
|
+
result+=" class='" + tokens.classes.join(" ") + "'";
|
118
|
+
}
|
119
|
+
return result + ">";
|
120
|
+
}
|
121
|
+
function closeTag( tokens ) {
|
122
|
+
if ( tokens.tag === undefined ) { return ""; }
|
123
|
+
return "</" + tokens.tag + ">";
|
124
|
+
}
|
125
|
+
|
126
|
+
result += openTag( node );
|
127
|
+
|
128
|
+
if ( self.innerHTML !== undefined ) {
|
129
|
+
result += self.innerHTML;
|
130
|
+
}
|
131
|
+
|
132
|
+
result += baconl.map( node.childNodes , function(index , childNode ){
|
133
|
+
return childNode.html();
|
134
|
+
} ).join("\n");
|
135
|
+
|
136
|
+
result += closeTag( node );
|
137
|
+
return result;
|
138
|
+
}
|
139
|
+
|
140
|
+
function parseDefintion(){
|
141
|
+
var element = baconl.parse( self.definition );
|
142
|
+
self.id = element.id;
|
143
|
+
self.classes = element.classes;
|
144
|
+
self.tag = element.tag;
|
145
|
+
self.innerHTML = element.innerHTML;
|
146
|
+
}
|
147
|
+
|
148
|
+
function makeChild( child ) {
|
149
|
+
child = baconl.node( child );
|
150
|
+
child.depth = self.depth + 1;
|
151
|
+
child.parentNode = self;
|
152
|
+
return child;
|
153
|
+
}
|
154
|
+
|
155
|
+
self.html = function() {
|
156
|
+
if ( arguments.length === 0 ) {
|
157
|
+
return htmlBody();
|
158
|
+
}
|
159
|
+
self.empty();
|
160
|
+
self.append.apply( self , arguments );
|
161
|
+
return self;
|
162
|
+
}
|
163
|
+
|
164
|
+
self.prepend = function() {
|
165
|
+
for ( var i = 0 ; i < arguments.length ; i++ ) {
|
166
|
+
self.childNodes.splice(0,0, makeChild( arguments[i] ) );
|
167
|
+
}
|
168
|
+
return self;
|
169
|
+
}
|
170
|
+
|
171
|
+
self.append = function() {
|
172
|
+
for ( var i = 0 ; i < arguments.length ; i++ ) {
|
173
|
+
self.childNodes.push( makeChild( arguments[i] ) );
|
174
|
+
}
|
175
|
+
return self;
|
176
|
+
}
|
177
|
+
|
178
|
+
self.remove = function() {
|
179
|
+
self.depth = 0;
|
180
|
+
if ( self.parentNode === undefined ) { return self; }
|
181
|
+
self.parentNode.childNodes = baconl.grep( self.parentNode.childNodes , function(i,node){
|
182
|
+
return node !== self;
|
183
|
+
} );
|
184
|
+
self.parentNode = undefined;
|
185
|
+
return self;
|
186
|
+
}
|
187
|
+
|
188
|
+
self.empty = function() {
|
189
|
+
baconl.each( self.childNodes , function(i,node){
|
190
|
+
node.remove();
|
191
|
+
});
|
192
|
+
return self;
|
193
|
+
}
|
194
|
+
|
195
|
+
self.hasClass = function( className ) {
|
196
|
+
for (var i=0; i < self.classes.length; i++) {
|
197
|
+
if ( self.classes[i] === className ) { return true; }
|
198
|
+
};
|
199
|
+
return false;
|
200
|
+
}
|
201
|
+
|
202
|
+
self.addClass = function( classes ) {
|
203
|
+
baconl.each( classes.split(/\s+/) , function(i , className) {
|
204
|
+
if ( !self.hasClass( className ) ) {
|
205
|
+
self.classes.push( className );
|
206
|
+
}
|
207
|
+
});
|
208
|
+
return self;
|
209
|
+
}
|
210
|
+
|
211
|
+
self.removeClass = function( classes ) {
|
212
|
+
baconl.each( classes.split(/\s+/) , function(index, argClassName) {
|
213
|
+
self.classes = baconl.grep( self.classes , function( index , ownClassName ) {
|
214
|
+
return (ownClassName !== argClassName);
|
215
|
+
} ) ;
|
216
|
+
});
|
217
|
+
return self;
|
218
|
+
}
|
219
|
+
|
220
|
+
parseDefintion();
|
221
|
+
return self;
|
222
|
+
}
|
223
|
+
|
224
|
+
baconl.baconize = function( unreadBuffer , parentNode ) {
|
225
|
+
if ( unreadBuffer.constructor === String ) {
|
226
|
+
unreadBuffer = unreadBuffer.split("\n");
|
227
|
+
}
|
228
|
+
|
229
|
+
if ( parentNode === undefined ) {
|
230
|
+
parentNode = baconl.node();
|
231
|
+
}
|
232
|
+
function lineDepth( line ) {
|
233
|
+
return line.match(/^\s*/)[0].length/2 + 1;
|
234
|
+
}
|
235
|
+
function isChild( line ) {
|
236
|
+
return lineDepth(line) === parentNode.depth+1;
|
237
|
+
}
|
238
|
+
|
239
|
+
for ( var i = 0 ; i < unreadBuffer.length ; i++ ) {
|
240
|
+
if ( isChild( unreadBuffer[i] ) ) {
|
241
|
+
var child = baconl.node( unreadBuffer[i] ) ;
|
242
|
+
parentNode.append( child );
|
243
|
+
baconl.baconize(
|
244
|
+
unreadBuffer.slice( i+1 ) ,
|
245
|
+
child
|
246
|
+
);
|
247
|
+
}
|
248
|
+
else if ( lineDepth(unreadBuffer[i]) <= parentNode.depth ) {
|
249
|
+
return parentNode;
|
250
|
+
}
|
251
|
+
}
|
252
|
+
return parentNode;
|
253
|
+
}
|
254
|
+
|
data/lib/jack/utils.js
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Jack.Utils = {
|
2
|
+
detect: function( array , callback ) {
|
3
|
+
for ( var i in array ) {
|
4
|
+
if ( callback.call( array[i] , i , array[i] ) ) {
|
5
|
+
return array[i];
|
6
|
+
}
|
7
|
+
}
|
8
|
+
return undefined;
|
9
|
+
} ,
|
10
|
+
camelize: function(string) {
|
11
|
+
var parts = string.split("_");
|
12
|
+
return parts.map(function(part) {
|
13
|
+
return part.substr(0,1).toUpperCase() + part.substr(1, part.length);
|
14
|
+
}).join("");
|
15
|
+
},
|
16
|
+
parameterize: function(string) {
|
17
|
+
var params = {};
|
18
|
+
|
19
|
+
if(null == string) return params;
|
20
|
+
var parameters = string.split("&");
|
21
|
+
for(var i = 0; i < parameters.length; i++) {
|
22
|
+
var bits = parameters[i].split("=");
|
23
|
+
params[bits[0]] = bits[1];
|
24
|
+
}
|
25
|
+
return params;
|
26
|
+
}
|
27
|
+
}
|
data/lib/jack/view.js
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Jack.Template = (function() {
|
2
|
+
var templates = {};
|
3
|
+
return function(name,file) {
|
4
|
+
if ( !file ) { return new DOMDocument(templates[name]); }
|
5
|
+
templates[name] = baconl(Ruby.File.read("./samples/framework/templates/index.html.haml"));
|
6
|
+
|
7
|
+
return Jack.Template;
|
8
|
+
}
|
9
|
+
})();
|
10
|
+
|
11
|
+
|
12
|
+
Jack.View = (function() {
|
13
|
+
var views = {};
|
14
|
+
return function(name,callback) {
|
15
|
+
if ( !callback ) {
|
16
|
+
return function() views[name].apply( window , arguments);
|
17
|
+
}
|
18
|
+
views[name] = callback;
|
19
|
+
return Jack.View;
|
20
|
+
}
|
21
|
+
})();
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Jack.Template( "index" , "./samples/framework/templates/index.html.haml" );
|
2
|
+
|
3
|
+
Jack.View( "indexView" , function( name ){
|
4
|
+
window.document = Jack.Template("index");
|
5
|
+
return $("body")
|
6
|
+
.find(".content")
|
7
|
+
.html(
|
8
|
+
baconl("%p This text was added dynamically by: ")
|
9
|
+
)
|
10
|
+
.append( name )
|
11
|
+
.end()
|
12
|
+
.append(
|
13
|
+
baconl("%ul.links")
|
14
|
+
)
|
15
|
+
.find(".links")
|
16
|
+
.append(
|
17
|
+
"<li><a href='/user/1'>User 1</a></li>" ,
|
18
|
+
"<li><a href='/user/2'>User 2</a></li>"
|
19
|
+
)
|
20
|
+
.end()
|
21
|
+
.end()
|
22
|
+
.html();
|
23
|
+
|
24
|
+
});
|
25
|
+
|
26
|
+
Jack.Action(/^\/$/,function() {
|
27
|
+
return Jack.View("indexView")("cohitre!!!!");
|
28
|
+
});
|
29
|
+
|
30
|
+
Jack.Action(/user\/\d+/,function() {
|
31
|
+
return baconl("%h1 User!");
|
32
|
+
});
|
33
|
+
|
34
|
+
Jack.up();
|
data/test/app.js
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env jack
|
2
|
+
|
3
|
+
var hello = function() {
|
4
|
+
return "<h1>O hai</h1>";
|
5
|
+
};
|
6
|
+
|
7
|
+
var error = function() {
|
8
|
+
return "11".boom();
|
9
|
+
}
|
10
|
+
|
11
|
+
Jack.Action(/o-hai/, hello);
|
12
|
+
Jack.Action(/pow/, error);
|
13
|
+
|
14
|
+
Jack.Action(/source\/app\.js/, function(env){
|
15
|
+
return [
|
16
|
+
200,
|
17
|
+
{"Content-Type": "text/plain"},
|
18
|
+
Ruby.File.read(Ruby.File.dirname(__FILE__)+'/app.js')
|
19
|
+
]
|
20
|
+
})
|
21
|
+
|
22
|
+
Jack.Action(/jquery/,function() {
|
23
|
+
try {
|
24
|
+
window.document = new DOMDocument( baconl("%div.message") );
|
25
|
+
window.$(".message").text("Hello from jquery");
|
26
|
+
return window.document.innerHTML;
|
27
|
+
}catch(e){
|
28
|
+
return 'framework is fine but jQuery builder is broke'
|
29
|
+
}
|
30
|
+
});
|
31
|
+
|
32
|
+
Jack.up()
|
data/test/test_jack.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require File.dirname(__FILE__) + '/../lib/jack'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
class TestJack < Test::Unit::TestCase
|
8
|
+
TEST_OPTS = OpenStruct.new(:log => File.dirname(__FILE__) + '/test.log',
|
9
|
+
:host => 'localhost', :port => 1338)
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@app_thread = Thread.new do
|
13
|
+
Jack.run(File.dirname(__FILE__) + '/app.js', TEST_OPTS)
|
14
|
+
end
|
15
|
+
sleep 1.0
|
16
|
+
end
|
17
|
+
|
18
|
+
def teardown
|
19
|
+
@app_thread.kill
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_hello_world
|
23
|
+
assert_equal( "<h1>O hai</h1>" , open('http://localhost:1338/o-hai').read )
|
24
|
+
assert_raises(OpenURI::HTTPError) { open('http://localhost:1338/pow').read }
|
25
|
+
assert_raises(OpenURI::HTTPError) { open('http://localhost:1338/404').read }
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_custom_content_type
|
29
|
+
open('http://localhost:1338/source/app.js') do |response|
|
30
|
+
assert_equal 'text/plain', response.content_type
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_views
|
35
|
+
assert_equal( "<DIV class='message'>Hello from jquery</DIV>" , open('http://localhost:1338/jquery').read )
|
36
|
+
end
|
37
|
+
|
38
|
+
if ENV['FULL_TEST']
|
39
|
+
def test_requests_dont_each_add_to_the_stack
|
40
|
+
n = 0
|
41
|
+
2_000.times do |n|
|
42
|
+
open('http://localhost:1338/o-hai').read
|
43
|
+
end
|
44
|
+
rescue => e
|
45
|
+
assert false, "Blew up after #{n} requests"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: JackDanger-jack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jack Danger Canty
|
8
|
+
- Phil Hagelberg
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-03-19 00:00:00 -07:00
|
14
|
+
default_executable: jack
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: jbarnette-johnson
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 1.0.0
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rack
|
28
|
+
type: :runtime
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.9.0
|
35
|
+
version:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: clip
|
38
|
+
type: :runtime
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 1.0.0
|
45
|
+
version:
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: minitest
|
48
|
+
type: :development
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.3.0
|
55
|
+
version:
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: hoe
|
58
|
+
type: :development
|
59
|
+
version_requirement:
|
60
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 1.11.0
|
65
|
+
version:
|
66
|
+
description:
|
67
|
+
email:
|
68
|
+
- gems@6brand.com
|
69
|
+
- technomancy@gmail.com
|
70
|
+
executables:
|
71
|
+
- jack
|
72
|
+
extensions: []
|
73
|
+
|
74
|
+
extra_rdoc_files:
|
75
|
+
- Manifest.txt
|
76
|
+
files:
|
77
|
+
- Manifest.txt
|
78
|
+
- README.markdown
|
79
|
+
- Rakefile
|
80
|
+
- bin/jack
|
81
|
+
- jack.gemspec
|
82
|
+
- lib/jack.js
|
83
|
+
- lib/jack.rb
|
84
|
+
- lib/jack/action.js
|
85
|
+
- lib/jack/baconl.js
|
86
|
+
- lib/jack/utils.js
|
87
|
+
- lib/jack/view.js
|
88
|
+
- samples/framework/boot.js
|
89
|
+
- samples/framework/templates/index.html.haml
|
90
|
+
- test/app.js
|
91
|
+
- test/test.log
|
92
|
+
- test/test_jack.rb
|
93
|
+
has_rdoc: true
|
94
|
+
homepage:
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options:
|
97
|
+
- --main
|
98
|
+
- README.txt
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: "0"
|
106
|
+
version:
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: "0"
|
112
|
+
version:
|
113
|
+
requirements: []
|
114
|
+
|
115
|
+
rubyforge_project: jack
|
116
|
+
rubygems_version: 1.2.0
|
117
|
+
signing_key:
|
118
|
+
specification_version: 2
|
119
|
+
summary: A web framework for Javascript.
|
120
|
+
test_files:
|
121
|
+
- test/test_jack.rb
|