spade 0.1.0 → 0.1.1.1
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/.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
|