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 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