spade 0.1.0 → 0.1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +0 -11
- data/.gitmodules +3 -6
- data/Gemfile +10 -0
- data/bin/spade +3 -1
- data/lib/spade.rb +70 -0
- data/lib/spade/bundle.rb +180 -0
- data/lib/spade/cli.rb +1 -17
- data/lib/spade/cli/base.rb +182 -0
- data/lib/spade/console.rb +39 -0
- data/lib/spade/context.rb +107 -0
- data/lib/spade/evaluator.rb +34 -0
- data/lib/spade/exports.rb +70 -0
- data/lib/spade/loader.rb +208 -0
- data/lib/spade/package/.gitignore +1 -0
- data/lib/spade/package/Gemfile +15 -0
- data/lib/spade/package/lib/spade.js +1283 -0
- data/lib/spade/package/lib/wrapper.js +15 -0
- data/lib/spade/package/package.json +17 -0
- data/lib/spade/package/spec/javascript/async-test.js +123 -0
- data/lib/spade/package/spec/javascript/compiler/javascript.js +13 -0
- data/lib/spade/package/spec/javascript/compiler/ruby.js +14 -0
- data/lib/spade/package/spec/javascript/loader-test.js +64 -0
- data/lib/spade/package/spec/javascript/normalize-test.js +73 -0
- data/lib/spade/package/spec/javascript/packages-test.js +50 -0
- data/lib/spade/package/spec/javascript/relative-require-test.js +72 -0
- data/lib/spade/package/spec/javascript/require-test.js +117 -0
- data/lib/spade/package/spec/javascript/sandbox/creation.js +44 -0
- data/lib/spade/package/spec/javascript/sandbox/evaluate.js +37 -0
- data/lib/spade/package/spec/javascript/sandbox/format.js +79 -0
- data/lib/spade/package/spec/javascript/sandbox/misc.js +58 -0
- data/lib/spade/package/spec/javascript/sandbox/preprocessor.js +81 -0
- data/lib/spade/package/spec/javascript/sandbox/require.js +48 -0
- data/lib/spade/package/spec/javascript/sandbox/run-command.js +21 -0
- data/lib/spade/package/spec/javascript/spade/externs.js +14 -0
- data/lib/spade/package/spec/javascript/spade/load-factory.js +15 -0
- data/lib/spade/package/spec/javascript/spade/misc.js +23 -0
- data/lib/spade/package/spec/javascript/spade/ready.js +12 -0
- data/lib/spade/package/spec/javascript/spade/register.js +13 -0
- data/lib/spade/package/spec/javascript_spec.rb +7 -0
- data/lib/spade/package/spec/spec_helper.rb +3 -0
- data/lib/spade/package/spec/support/core_test.rb +67 -0
- data/lib/spade/reactor.rb +159 -0
- data/lib/spade/server.rb +66 -0
- data/lib/spade/shell.rb +85 -0
- data/lib/spade/version.rb +1 -1
- data/spade.gemspec +15 -4
- data/spec/cli/update_spec.rb +65 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/cli.rb +103 -0
- data/spec/support/matchers.rb +12 -0
- data/spec/support/path.rb +66 -0
- metadata +146 -78
- data/.rspec +0 -1
- data/Buildfile +0 -18
- data/README.md +0 -152
- data/Rakefile +0 -9
- data/examples/format-app/lib/hello.coffee +0 -1
- data/examples/format-app/lib/main.js +0 -6
- data/examples/format-app/package.json +0 -10
- data/examples/format-app/resources/README.txt +0 -1
- data/examples/format-app/resources/config.json +0 -3
- data/examples/path-test/lib/hello.js +0 -1
- data/examples/path-test/lib/main.js +0 -1
- data/examples/path-test/package.json +0 -5
- data/examples/sc-app/index.html +0 -13
- data/examples/sc-app/lib/main.js +0 -24
- data/examples/sc-app/package.json +0 -8
- data/examples/single-file.js +0 -22
- data/examples/todos/index.html +0 -11
- data/examples/todos/lib/main.js +0 -11
- data/examples/todos/lib/todos.js +0 -93
- data/examples/todos/package.json +0 -10
- data/examples/todos/resources/stylesheets/todos.css +0 -162
- data/examples/todos/resources/templates/todos.handlebars +0 -31
- data/examples/web-app/README.md +0 -83
- data/examples/web-app/index.html +0 -12
- data/examples/web-app/lib/main.js +0 -3
- data/examples/web-app/package.json +0 -6
- data/examples/web-app/tests.html +0 -12
- data/examples/web-app/tests/ct-example-test.js +0 -39
- data/examples/web-app/tests/qunit-test.js +0 -23
@@ -0,0 +1,39 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
|
8
|
+
module Spade
|
9
|
+
|
10
|
+
class Console
|
11
|
+
|
12
|
+
def debug(*args)
|
13
|
+
puts "\033[35mDEBUG: #{args * ','}\033[m"
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def info(*args)
|
18
|
+
puts args * ','
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def error(*args)
|
23
|
+
puts "\033[31mERROR: #{args * ','}\033[m"
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def warn(*args)
|
28
|
+
puts "\033[33mWARN: #{args * ','}\033[m"
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def log(*args)
|
33
|
+
puts args * ','
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
require 'v8'
|
8
|
+
|
9
|
+
module Spade
|
10
|
+
|
11
|
+
# Creates a basic context suitable for running modules. The environments
|
12
|
+
# setup in this context will mimic a browser worker thread context,
|
13
|
+
# including timeouts and a console. A navigator object is also defined
|
14
|
+
# that provides some general information about the context.
|
15
|
+
class Context < V8::Context
|
16
|
+
|
17
|
+
attr_reader :reactor
|
18
|
+
attr_reader :verbose
|
19
|
+
|
20
|
+
def require(mod_name)
|
21
|
+
self.eval("require('#{mod_name}');");
|
22
|
+
end
|
23
|
+
|
24
|
+
# Load the spade and racer-loader.
|
25
|
+
def initialize(opts={})
|
26
|
+
@reactor = opts[:reactor]
|
27
|
+
@verbose = opts[:verbose]
|
28
|
+
super(opts) do |ctx|
|
29
|
+
ctx['reactor'] = @reactor
|
30
|
+
ctx['console'] = Console.new
|
31
|
+
ctx['window'] = ctx.scope
|
32
|
+
ctx.eval %[
|
33
|
+
(function() {
|
34
|
+
var r = reactor;
|
35
|
+
setTimeout = function(c,i) { return r.set_timeout(c,i); };
|
36
|
+
setInterval = function(c,i) { return r.set_interval(c,i); };
|
37
|
+
clearTimeout = function(t) { return r.clear_timeout(t); };
|
38
|
+
clearInterval = function(t) { return r.clear_interval(t); };
|
39
|
+
navigator = {
|
40
|
+
appName: 'spade',
|
41
|
+
appVersion: "#{Spade::VERSION}",
|
42
|
+
platform: "#{RUBY_PLATFORM}",
|
43
|
+
userAgent: 'spade #{Spade::VERSION}; #{RUBY_PLATFORM}'
|
44
|
+
}
|
45
|
+
|
46
|
+
exit = function(status) { return r.exit(status || 0); };
|
47
|
+
})();
|
48
|
+
]
|
49
|
+
|
50
|
+
ctx['reactor'] = nil
|
51
|
+
|
52
|
+
yield(ctx) if block_given?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
# The primary context created when running spade exec or spade console.
|
59
|
+
# This context will also automatically start a reactor loop.
|
60
|
+
class MainContext < Context
|
61
|
+
|
62
|
+
attr_accessor :rootdir
|
63
|
+
attr_accessor :caller_id
|
64
|
+
|
65
|
+
# Load the spade and racer-loader.
|
66
|
+
def initialize(opts={})
|
67
|
+
env = opts[:env] || ENV
|
68
|
+
@rootdir = opts[:rootdir] || opts['rootdir']
|
69
|
+
@caller_id = opts[:caller_id] || opts['caller_id']
|
70
|
+
@reactor = opts[:reactor] ||= Reactor.new(self)
|
71
|
+
lang = opts[:language] ||= (env['LANG']||'en_US').gsub(/\..*/, '')
|
72
|
+
lang = lang.gsub '_', '-'
|
73
|
+
|
74
|
+
|
75
|
+
super(opts) do |ctx|
|
76
|
+
ctx['ENV'] = env.to_hash
|
77
|
+
ctx['ENV']['SPADE_PLATFORM'] = { 'ENGINE' => 'spade-ruby' }
|
78
|
+
ctx['ENV']['LANG'] = lang
|
79
|
+
|
80
|
+
ctx['ARGV'] = opts[:argv] || ARGV
|
81
|
+
|
82
|
+
# Load spade and patch in evaluator and loader plugins
|
83
|
+
ctx.load(Spade.jspath)
|
84
|
+
ctx['rubyLoader'] = Loader.new(self)
|
85
|
+
ctx['rubyEvaluator'] = Evaluator.new(self)
|
86
|
+
|
87
|
+
ctx.eval %[
|
88
|
+
spade.loader = rubyLoader;
|
89
|
+
spade.evaluator = rubyEvaluator;
|
90
|
+
spade.defaultSandbox.rootdir = #{@rootdir.to_json};
|
91
|
+
spade.globalize();
|
92
|
+
]
|
93
|
+
|
94
|
+
ctx.eval("spade.defaultSandbox.callerId = #{@caller_id.to_json};") if @caller_id
|
95
|
+
|
96
|
+
ctx['rubyLoader'] = ctx['rubyEvaluator'] = nil
|
97
|
+
|
98
|
+
@reactor.start do
|
99
|
+
yield(self) if block_given?
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
module Spade
|
8
|
+
|
9
|
+
# Evaluator plugin for the default context. Know how to create a new
|
10
|
+
# isolated context for the object
|
11
|
+
class Evaluator
|
12
|
+
|
13
|
+
def initialize(ctx)
|
14
|
+
@ctx = ctx
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup(sandbox)
|
18
|
+
if sandbox['isIsolated']
|
19
|
+
sandbox['ctx'] = Context.new :reactor => @ctx.reactor
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def evaluate(data, sandbox, filename)
|
24
|
+
ctx = sandbox['ctx'] || @ctx
|
25
|
+
ctx.eval(data, filename)
|
26
|
+
end
|
27
|
+
|
28
|
+
def teardown(sandbox)
|
29
|
+
sandbox['ctx'] = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
module Spade
|
8
|
+
module Namespace
|
9
|
+
|
10
|
+
def [](name)
|
11
|
+
begin
|
12
|
+
self.class.const_defined?(name) ? self.class.const_get(name) : yield
|
13
|
+
rescue NameError => e
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class Exports
|
21
|
+
|
22
|
+
attr_reader :context
|
23
|
+
|
24
|
+
def initialize(ctx)
|
25
|
+
@context = ctx
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](name)
|
29
|
+
|
30
|
+
begin
|
31
|
+
if self.class.const_defined?(name)
|
32
|
+
ret = self.class.const_get(name)
|
33
|
+
|
34
|
+
# If we are returning a class, create a custom subclass the first
|
35
|
+
# time that also exposes the current context.
|
36
|
+
if ret.instance_of? Class
|
37
|
+
@klass_cache ||= {}
|
38
|
+
unless @klass_cache[name]
|
39
|
+
|
40
|
+
proc1 = proc { @context }
|
41
|
+
@klass_cache[name] = Class.new(ret) do
|
42
|
+
|
43
|
+
@context = proc1.call
|
44
|
+
|
45
|
+
def self.context
|
46
|
+
@context
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
@klass_cache[name]
|
54
|
+
else
|
55
|
+
ret
|
56
|
+
end
|
57
|
+
|
58
|
+
else
|
59
|
+
yield
|
60
|
+
end
|
61
|
+
|
62
|
+
rescue NameError => e
|
63
|
+
yield
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
data/lib/spade/loader.rb
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
|
10
|
+
module Spade
|
11
|
+
|
12
|
+
class Loader
|
13
|
+
|
14
|
+
def initialize(ctx)
|
15
|
+
@ctx = ctx
|
16
|
+
end
|
17
|
+
|
18
|
+
def discoverRoot(path)
|
19
|
+
Spade.discover_root path
|
20
|
+
end
|
21
|
+
|
22
|
+
def root(path=nil)
|
23
|
+
return @ctx.rootdir if path.nil?
|
24
|
+
@ctx.rootdir = path
|
25
|
+
@packages = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# exposed to JS. Find the JS file on disk and register the module
|
29
|
+
def loadFactory(spade, id, formats, done=nil)
|
30
|
+
formats = formats.to_a # We may get a V8::Array, we want a normal one
|
31
|
+
|
32
|
+
# load individual files
|
33
|
+
if id =~ /^file:\//
|
34
|
+
js_path = id[5..-1]
|
35
|
+
if File.exists? js_path
|
36
|
+
load_module id, js_path, ['js'], js_path
|
37
|
+
end
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
|
41
|
+
parts = id.split '/'
|
42
|
+
package_name = parts.shift
|
43
|
+
package_info = packages[package_name]
|
44
|
+
skip_module = false
|
45
|
+
|
46
|
+
return nil if package_info.nil?
|
47
|
+
|
48
|
+
if parts.size==1 && parts[0] == '~package'
|
49
|
+
skip_module = true
|
50
|
+
elsif parts.size==1 && parts[0] == 'main'
|
51
|
+
parts = (package_info[:json]['main'] || 'lib/main').split('/')
|
52
|
+
dirs = [parts[0...-1]]
|
53
|
+
parts = [parts[-1]]
|
54
|
+
else
|
55
|
+
dirs = extract_dirs(parts, package_info)
|
56
|
+
end
|
57
|
+
|
58
|
+
# register the package first - also make sure dependencies are
|
59
|
+
# registered since they are needed for loading plugins
|
60
|
+
unless package_info[:registered]
|
61
|
+
package_info[:registered] = true
|
62
|
+
@ctx.eval "spade.register('#{package_name}', #{package_info[:json].to_json});"
|
63
|
+
|
64
|
+
deps = package_info[:json]['dependencies'] || [];
|
65
|
+
deps.each do |dep_name, ignored|
|
66
|
+
dep_package_info = packages[dep_name]
|
67
|
+
next unless dep_package_info && !dep_package_info[:registered]
|
68
|
+
dep_package_info[:registered] = true
|
69
|
+
@ctx.eval "spade.register('#{dep_name}', #{dep_package_info[:json].to_json});"
|
70
|
+
|
71
|
+
# Add new formats if they are specified in our dependencies
|
72
|
+
dep_formats = dep_package_info[:json]['plugin:formats']
|
73
|
+
formats.unshift(*dep_formats.keys).uniq! if dep_formats
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
unless skip_module
|
79
|
+
filename = parts.pop
|
80
|
+
base_path = package_info[:path]
|
81
|
+
formats << ['js'] if formats.empty?
|
82
|
+
|
83
|
+
dirs.each do |dirname|
|
84
|
+
formats.each do |fmt|
|
85
|
+
cur_path = File.join(base_path, dirname, parts, filename+'.'+fmt)
|
86
|
+
if File.exist? cur_path
|
87
|
+
load_module id, cur_path, fmt, cur_path
|
88
|
+
return nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
rb_path = File.join(package_info[:path],dirname,parts, filename+'.rb')
|
93
|
+
if File.exists? rb_path
|
94
|
+
load_ruby id, rb_path
|
95
|
+
return nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
return nil
|
101
|
+
end
|
102
|
+
|
103
|
+
# exposed to JS. Determines if the named id exists in the system
|
104
|
+
def exists(spade, id, formats)
|
105
|
+
|
106
|
+
# individual files
|
107
|
+
return File.exists?(id[5..-1]) if id =~ /^file:\//
|
108
|
+
|
109
|
+
parts = id.split '/'
|
110
|
+
package_name = parts.shift
|
111
|
+
package_info = packages[package_name]
|
112
|
+
|
113
|
+
return false if package_info.nil?
|
114
|
+
return true if parts.size==1 && parts[0] == '~package'
|
115
|
+
|
116
|
+
dirs = extract_dirs(parts, package_info)
|
117
|
+
|
118
|
+
|
119
|
+
filename = parts.pop
|
120
|
+
base_path = package_info[:path]
|
121
|
+
formats = ['js'] if formats.nil?
|
122
|
+
dirs.each do |dirname|
|
123
|
+
formats.each do |fmt|
|
124
|
+
cur_path = File.join(base_path, dirname, parts, filename+'.'+fmt)
|
125
|
+
return true if File.exist? cur_path
|
126
|
+
end
|
127
|
+
|
128
|
+
rb_path = File.join(package_info[:path],dirname,parts, filename+'.rb')
|
129
|
+
return File.exists? rb_path
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def load_module(id, module_path, format, path)
|
134
|
+
module_contents = File.read(module_path).to_json # encode as string
|
135
|
+
@ctx.eval("spade.register('#{id}',#{module_contents}, { format: #{format.to_s.to_json}, filename: #{path.to_s.to_json} });")
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
|
139
|
+
def load_ruby(id, rb_path)
|
140
|
+
|
141
|
+
klass = Spade.exports_for rb_path
|
142
|
+
exports = klass.nil? ? {} : klass.new(@ctx)
|
143
|
+
@ctx['$__rb_exports__'] = exports
|
144
|
+
|
145
|
+
@ctx.eval(%[(function() {
|
146
|
+
var exp = $__rb_exports__;
|
147
|
+
spade.register('#{id}', function(r,e,m) { m.exports = exp; });
|
148
|
+
})();])
|
149
|
+
|
150
|
+
@ctx['$__rb_exports__'] = nil
|
151
|
+
end
|
152
|
+
|
153
|
+
def packages
|
154
|
+
@packages unless @packages.nil?
|
155
|
+
@packages = {}
|
156
|
+
|
157
|
+
if defined?(BPM)
|
158
|
+
package_paths = Dir.glob(File.join(LibGems.dir, 'gems', '*'))
|
159
|
+
package_paths.each{|path| add_package(path) }
|
160
|
+
end
|
161
|
+
|
162
|
+
# in reverse order of precedence
|
163
|
+
%w[.spade/packages vendor/cache vendor/packages packages].each do |p|
|
164
|
+
package_paths = Dir.glob File.join(@ctx.rootdir, p.split('/'), '*')
|
165
|
+
package_paths.each { |path| add_package(path) }
|
166
|
+
end
|
167
|
+
|
168
|
+
# add self
|
169
|
+
add_package @ctx.rootdir
|
170
|
+
|
171
|
+
@packages
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def extract_dirs(parts, package_info)
|
177
|
+
dirname = (parts.size > 0 && parts.first.chars.first == '~') ?
|
178
|
+
parts.shift[1..-1] : 'lib'
|
179
|
+
|
180
|
+
dirs = package_info[:directories][dirname]
|
181
|
+
raise "Can't require from unknown directory: #{dirname}" unless dirs
|
182
|
+
dirs = [dirs] unless dirs.is_a?(Array)
|
183
|
+
|
184
|
+
dirs
|
185
|
+
end
|
186
|
+
|
187
|
+
def add_package(path)
|
188
|
+
json_package = File.join(path, 'package.json')
|
189
|
+
return unless File.exists?(json_package)
|
190
|
+
|
191
|
+
json = JSON.load(File.read(json_package)) rescue nil
|
192
|
+
return if json.nil?
|
193
|
+
|
194
|
+
directories = json["directories"] || { "lib" => "lib" }
|
195
|
+
json["root"] = "file:/"+File.split(path).join('/')
|
196
|
+
@packages[json["name"]] = {
|
197
|
+
:registered => false,
|
198
|
+
:path => path,
|
199
|
+
:directories => directories,
|
200
|
+
:json => json
|
201
|
+
}
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
end
|