bluenode 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 76a6c00ed8a1f68bec728bc59028ebfcbf20535a
4
+ data.tar.gz: 5a83087d85a05e5f89ced711c3451f6ab7c8ee72
5
+ SHA512:
6
+ metadata.gz: 6b4f14a41e278b967b9e01c394d26a25b1b410d46a1291d405ef1aab600554d4bb82420bc741e168fb1b0dca85a6c284fc0b9f1ba646110261a0143f9571ce76
7
+ data.tar.gz: 5a961a4a15eec7785327a80df26f1a03673adfd4e0f1d57e2fc265cbf39060e965a3c983581a0fad7cabdead52fd1915a127318e7c8e3fbd39917517555b6b7c
data/.gitignore ADDED
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ Gemfile.lock
30
+ .ruby-version
31
+ .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bluenode.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Martin Andert
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Bluenode
2
+
3
+ A subset of Node.js running in therubyracer. More info coming soon.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'bluenode'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install bluenode
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/martinandert/bluenode/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/bluenode.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bluenode/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'bluenode'
8
+ spec.version = Bluenode::VERSION
9
+ spec.authors = ['Martin Andert']
10
+ spec.email = ['mandert@gmail.com']
11
+ spec.summary = 'A subset of Node.js running in therubyracer.'
12
+ spec.description = 'A subset of Node.js running in therubyracer.'
13
+ spec.homepage = 'https://github.com/martinandert/bluenode'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.7'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+
24
+ spec.add_dependency 'therubyracer', '~> 0.12.1'
25
+ spec.add_dependency 'ref'
26
+ end
data/lib/bluenode.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'bluenode/version'
2
+ require 'v8'
3
+ require 'ref'
4
+
5
+ # do not use TRR's built-in weakref implementation
6
+ V8::Weak.send :remove_const, :Ref
7
+ V8::Weak::Ref = Ref::WeakReference
8
+ V8::Weak.send :remove_const, :WeakValueMap
9
+ V8::Weak::WeakValueMap = Ref::WeakValueMap
10
+
11
+ module Bluenode
12
+ autoload :Context, 'bluenode/context'
13
+ autoload :Module, 'bluenode/module'
14
+ autoload :NativeModule, 'bluenode/native_module'
15
+ end
@@ -0,0 +1,278 @@
1
+ require 'json'
2
+ require 'rbconfig'
3
+
4
+ module Bluenode
5
+ class Context
6
+ class << self
7
+ def main(basedir = Dir.pwd)
8
+ context = new(basedir)
9
+ context.require './'
10
+ end
11
+
12
+ def node_module_paths(from)
13
+ from = File.expand_path(from)
14
+
15
+ split_re = windows? && /[\/\\]/ || /\//
16
+ paths = []
17
+ parts = from.split(split_re)
18
+ tip = parts.size - 1
19
+
20
+ while tip >= 0
21
+ if parts[tip] != 'node_modules'
22
+ dir = File.join(*parts[0..tip], 'node_modules')
23
+ paths << dir
24
+ end
25
+
26
+ tip -= 1
27
+ end
28
+
29
+ paths
30
+ end
31
+
32
+ def windows?
33
+ @windows ||= !!(RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
34
+ end
35
+ end
36
+
37
+ attr_reader :runtime, :basedir
38
+
39
+ def initialize(basedir = Dir.pwd, env = ENV.dup, stdout = $stdout, stderr = $stderr)
40
+ @runtime = V8::Context.new
41
+ @basedir = basedir.to_s
42
+
43
+ startup env, stdout, stderr
44
+ end
45
+
46
+ def process
47
+ @runtime['process']
48
+ end
49
+
50
+ def eval_as_module(script, name = '[eval]', is_main = false)
51
+ Module.new(self, name).tap do |mod|
52
+ mod.filename = File.join(basedir, name)
53
+ mod.paths = self.class.node_module_paths(basedir)
54
+
55
+ if is_main
56
+ runtime['process'].mainModule = mod
57
+ mod.id = '.'
58
+ end
59
+
60
+ mod.send :compile, script, "#{name}-wrapper"
61
+ end
62
+ end
63
+
64
+ def require(path)
65
+ script = "module.exports = require('#{path}')"
66
+ mod = eval_as_module(script, '[main]', true)
67
+
68
+ mod.exports
69
+ end
70
+
71
+ def new_object
72
+ @runtime['Object'].new
73
+ end
74
+
75
+ def new_function(callable, props = {})
76
+ @runtime.enter do
77
+ function = @runtime.to_v8(callable)
78
+
79
+ props.each do |name, value|
80
+ function.Set @runtime.to_v8(name.to_s), @runtime.to_v8(value)
81
+ end
82
+
83
+ function
84
+ end
85
+ end
86
+
87
+ def modules
88
+ @modules ||= {}
89
+ end
90
+
91
+ def extensions
92
+ @extensions ||= {}.tap do |extensions|
93
+ extensions['.js'] = lambda do |mod, filename|
94
+ mod.send :compile, File.read(filename), filename
95
+ end
96
+
97
+ extensions['.json'] = lambda do |mod, filename|
98
+ begin
99
+ mod.exports = JSON.parse(File.read(filename))
100
+ rescue => exc
101
+ raise $!, "#{filename}: #{$!}", $!.backtrace
102
+ end
103
+ end
104
+
105
+ extensions['.node'] = lambda do |mod, filename|
106
+ raise 'requiring modules with a .node extension is not supported'
107
+ end
108
+ end
109
+ end
110
+
111
+ def global_paths
112
+ @global_paths ||= begin
113
+ home_dir = self.class.windows? && ENV['USERPROFILE'] || ENV['HOME']
114
+ exec_path = `which node`.chomp
115
+
116
+ paths = [File.expand_path(File.join('..', '..', 'lib', 'node'), exec_path)]
117
+
118
+ if home_dir
119
+ paths.unshift File.expand_path('.node_libraries', home_dir)
120
+ paths.unshift File.expand_path('.node_modules', home_dir)
121
+ end
122
+
123
+ if node_path = ENV['NODE_PATH']
124
+ paths = node_path.split(File::PATH_SEPARATOR).concat(paths)
125
+ end
126
+
127
+ paths
128
+ end
129
+ end
130
+
131
+ def find_path(request, paths)
132
+ exts = extensions.keys
133
+ paths = [''] if request[0] == '/'
134
+ trailing_slash = request[-1] == '/'
135
+ cache_key = { request: request, paths: paths }.to_json
136
+
137
+ return path_cache[cache_key] if path_cache.key?(cache_key)
138
+
139
+ i, ii = 0, paths.size
140
+
141
+ while i < ii
142
+ base_path = File.expand_path(request, paths[i])
143
+ filename = nil
144
+
145
+ unless trailing_slash
146
+ filename = try_file(base_path)
147
+ filename = try_extensions(base_path, exts) unless filename
148
+ end
149
+
150
+ filename = try_package(base_path, exts) unless filename
151
+ filename = try_extensions(File.expand_path('index', base_path), exts) unless filename
152
+
153
+ if filename
154
+ path_cache[cache_key] = filename
155
+ return filename
156
+ end
157
+
158
+ i += 1
159
+ end
160
+
161
+ false
162
+ end
163
+
164
+ private
165
+ def path_cache
166
+ @path_cache ||= {}
167
+ end
168
+
169
+ def realpath_cache
170
+ @realpath_cache ||= {}
171
+ end
172
+
173
+ def package_main_cache
174
+ @package_main_cache ||= {}
175
+ end
176
+
177
+ def try_file(path)
178
+ return false unless File.file?(path)
179
+
180
+ realpath_cache[path] ||= File.realpath(path)
181
+ end
182
+
183
+ def try_extensions(path, exts)
184
+ i, ii = 0, exts.size
185
+
186
+ while i < ii
187
+ filename = try_file(path + exts[i])
188
+ return filename if filename
189
+
190
+ i += 1
191
+ end
192
+
193
+ false
194
+ end
195
+
196
+ def try_package(path, exts)
197
+ pkg = read_package(path)
198
+
199
+ return false unless pkg
200
+
201
+ filename = File.expand_path(pkg, path)
202
+
203
+ try_file(filename) || try_extensions(filename, exts) || try_extensions(File.expand_path('index', filename), exts)
204
+ end
205
+
206
+ def read_package(path)
207
+ return package_main_cache[path] if package_main_cache.key?(path)
208
+
209
+ begin
210
+ json_path = File.expand_path('package.json', path)
211
+ json = File.read(json_path)
212
+ rescue => exc
213
+ return false
214
+ end
215
+
216
+ package_main_cache[path] = JSON.parse(json)['main']
217
+ rescue => exc
218
+ raise $!, "error parsing #{json_path}: #{$!}", $!.backtrace
219
+ end
220
+
221
+ def startup(env, stdout, stderr)
222
+ function = runtime.eval <<-EOS
223
+ var global = this;
224
+
225
+ (function(process) {
226
+ global.process = process;
227
+ global.global = global;
228
+ global.GLOBAL = global;
229
+ global.root = global;
230
+ global.Buffer = function Buffer() { throw new Error('global.Buffer is not supported'); };
231
+
232
+ function noop() {};
233
+
234
+ process.domain = null;
235
+ process.on = noop;
236
+ process.once = noop;
237
+ process.off = noop;
238
+ process.addListener = noop;
239
+ process.removeListener = noop;
240
+ process.removeAllListeners = noop;
241
+ process.emit = noop;
242
+
243
+ process.chdir = function(dir) {
244
+ throw new Error('process.chdir is not supported');
245
+ };
246
+
247
+ process.umask = function() { return 0; };
248
+ })
249
+ EOS
250
+
251
+ process = {
252
+ title: $0,
253
+ pid: Process.pid,
254
+ env: env,
255
+
256
+ cwd: lambda { |*args| basedir },
257
+
258
+ stdout: {
259
+ write: lambda do |_, chunk, *args|
260
+ stdout.write chunk
261
+ true
262
+ end
263
+ },
264
+
265
+ stderr: {
266
+ write: lambda do |_, chunk, *args|
267
+ stderr.write chunk
268
+ true
269
+ end
270
+ }
271
+ }
272
+
273
+ function.call process
274
+
275
+ runtime['global'].console = NativeModule.require(self, 'console')
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,137 @@
1
+ module Bluenode
2
+ class Module
3
+ class << self
4
+ def wrap(script)
5
+ "(function(exports, require, module, __filename, __dirname) {\n#{script}\n})"
6
+ end
7
+ end
8
+
9
+ attr_accessor :id, :filename, :exports, :loaded, :paths
10
+ attr_reader :parent, :children
11
+ alias :loaded? :loaded
12
+
13
+ def initialize(context, id, parent = nil)
14
+ @context = context
15
+ @id = id
16
+ @exports = context.new_object
17
+ @parent = parent
18
+
19
+ parent.children << self if parent
20
+
21
+ @filename = nil
22
+ @loaded = false
23
+ @children = []
24
+ @paths = []
25
+ end
26
+
27
+ def load(filename)
28
+ raise LoadError, 'module already loaded' if loaded?
29
+
30
+ @filename = filename
31
+ @paths = @context.class.node_module_paths(File.dirname(filename))
32
+
33
+ extension = File.extname(filename)
34
+ extension = '.js' if extension.nil? || extension == ''
35
+ extension = '.js' unless @context.extensions.key?(extension)
36
+
37
+ @context.extensions[extension].call self, filename
38
+
39
+ @loaded = true
40
+ end
41
+
42
+ def require(request)
43
+ filename = resolve(request)
44
+ cached = @context.modules[filename]
45
+
46
+ return cached.exports if cached
47
+
48
+ return NativeModule.require(@context, filename) if NativeModule.exist?(filename)
49
+
50
+ mod = self.class.new(@context, filename, self)
51
+
52
+ @context.modules[filename] = mod
53
+
54
+ had_exception = true
55
+
56
+ begin
57
+ mod.load filename
58
+ had_exception = false
59
+ ensure
60
+ @context.modules.delete(filename) if had_exception
61
+ end
62
+
63
+ mod.exports
64
+ end
65
+
66
+ private
67
+ def resolve(request)
68
+ return request if NativeModule.exist?(request)
69
+
70
+ id, paths = resolve_lookup_paths(request)
71
+ filename = @context.find_path(request, paths)
72
+
73
+ filename or raise LoadError, "cannot find module '#{request}'"
74
+ end
75
+
76
+ def compile(content, filename)
77
+ props = {
78
+ main: @context.process.mainModule,
79
+ cache: @context.modules,
80
+ extensions: @context.extensions,
81
+ resolve: resolve_proc
82
+ }
83
+
84
+ wrapped = self.class.wrap(content.sub(/^#!.*/, ''))
85
+ function = @context.runtime.eval(wrapped, filename)
86
+ dirname = File.dirname(filename)
87
+ v8_require = @context.new_function(require_proc, props)
88
+ args = [exports, v8_require, self, filename, dirname]
89
+
90
+ function.methodcall exports, *args
91
+ end
92
+
93
+ def require_proc
94
+ @require_proc ||= lambda do |_, path|
95
+ raise LoadError, 'path must be a string' unless path.is_a?(String)
96
+
97
+ require path
98
+ end
99
+ end
100
+
101
+ def resolve_proc
102
+ @resolve_proc ||= lambda do |_, request|
103
+ resolve request
104
+ end
105
+ end
106
+
107
+ def resolve_lookup_paths(request)
108
+ return [request, []] if NativeModule.exist?(request)
109
+
110
+ start = request[0..1]
111
+
112
+ if start != './' && start != '..'
113
+ main_paths = @context.global_paths
114
+ main_paths = paths.concat(main_paths)
115
+
116
+ return [request, main_paths]
117
+ end
118
+
119
+ unless filename
120
+ main_paths = ['.'].concat(@context.global_paths)
121
+ main_paths = @context.class.node_module_paths('.').concat(main_paths)
122
+
123
+ return [request, main_paths]
124
+ end
125
+
126
+ is_index = File.basename(filename) =~ /^index(\.\w+)*$/
127
+ id_path = is_index ? id : File.dirname(id)
128
+ id = File.expand_path(request, id_path)
129
+
130
+ if id_path == '.' && id.index('/').nil?
131
+ id = './' + id
132
+ end
133
+
134
+ [id, [File.dirname(filename)]]
135
+ end
136
+ end
137
+ end