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 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
@@ -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
+ })();
@@ -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();
@@ -0,0 +1,6 @@
1
+ %html
2
+ %title Hello!
3
+ %body
4
+ %h1 Garrulous Gorilla
5
+ %div.content
6
+
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