bluenode 0.0.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.
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