JackDanger-jack 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|