opal 0.7.2 → 0.8.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -1
- data/CHANGELOG.md +29 -0
- data/CONTRIBUTING.md +51 -4
- data/Gemfile +3 -0
- data/README.md +5 -5
- data/config.ru +1 -1
- data/examples/sinatra/Gemfile +1 -0
- data/examples/sinatra/config.ru +13 -3
- data/lib/mspec/opal/rake_task.rb +21 -30
- data/lib/mspec/opal/runner.rb +37 -0
- data/lib/mspec/opal/special_calls.rb +6 -0
- data/lib/opal/builder.rb +1 -0
- data/lib/opal/builder_processors.rb +5 -2
- data/lib/opal/cli_runners/phantom.js +10 -1
- data/lib/opal/compiler.rb +6 -3
- data/lib/opal/config.rb +48 -0
- data/lib/opal/nodes/call.rb +3 -2
- data/lib/opal/nodes/literal.rb +19 -2
- data/lib/opal/parser/grammar.rb +2224 -2196
- data/lib/opal/parser/grammar.y +25 -7
- data/lib/opal/parser/lexer.rb +12 -9
- data/lib/opal/path_reader.rb +1 -1
- data/lib/opal/sprockets/erb.rb +6 -20
- data/lib/opal/sprockets/path_reader.rb +4 -2
- data/lib/opal/sprockets/processor.rb +135 -80
- data/lib/opal/sprockets/server.rb +49 -78
- data/lib/opal/sprockets/source_map_header_patch.rb +41 -0
- data/lib/opal/sprockets/source_map_server.rb +115 -0
- data/lib/opal/version.rb +1 -1
- data/lib/tilt/opal.rb +48 -0
- data/opal.gemspec +1 -1
- data/opal/corelib/array.rb +179 -51
- data/opal/corelib/array/inheritance.rb +14 -0
- data/opal/corelib/boolean.rb +5 -0
- data/opal/corelib/hash.rb +1 -1
- data/opal/corelib/kernel.rb +660 -164
- data/opal/corelib/match_data.rb +44 -21
- data/opal/corelib/module.rb +83 -53
- data/opal/corelib/numeric.rb +15 -1
- data/opal/corelib/regexp.rb +31 -75
- data/opal/corelib/runtime.js +20 -8
- data/opal/corelib/string.rb +754 -243
- data/opal/corelib/string/inheritance.rb +20 -3
- data/opal/corelib/struct.rb +30 -6
- data/opal/corelib/variables.rb +2 -2
- data/spec/filters/bugs/array.rb +0 -39
- data/spec/filters/bugs/kernel.rb +10 -7
- data/spec/filters/bugs/module.rb +21 -0
- data/spec/filters/bugs/opal.rb +0 -5
- data/spec/filters/bugs/singleton.rb +0 -2
- data/spec/filters/bugs/string.rb +69 -315
- data/spec/filters/bugs/struct.rb +0 -16
- data/spec/filters/unsupported/encoding.rb +7 -0
- data/spec/filters/unsupported/float.rb +3 -0
- data/spec/filters/unsupported/integer_size.rb +52 -0
- data/spec/filters/unsupported/marshal.rb +4 -0
- data/spec/filters/unsupported/mutable_strings.rb +37 -0
- data/spec/filters/unsupported/private_methods.rb +11 -0
- data/spec/filters/unsupported/rational_numbers.rb +4 -0
- data/spec/filters/unsupported/regular_expressions.rb +47 -0
- data/spec/filters/unsupported/symbols.rb +7 -0
- data/spec/filters/unsupported/tainted.rb +23 -1
- data/spec/filters/unsupported/trusted.rb +5 -0
- data/spec/lib/fixtures/complex_sprockets.js.rb.erb +4 -0
- data/spec/lib/fixtures/jst_file.js.jst +1 -0
- data/spec/lib/parser/call_spec.rb +19 -0
- data/spec/lib/parser/def_spec.rb +6 -0
- data/spec/lib/sprockets/erb_spec.rb +17 -4
- data/spec/lib/sprockets/processor_spec.rb +50 -18
- data/spec/lib/sprockets/server_spec.rb +39 -11
- data/spec/lib/tilt/opal_spec.rb +19 -0
- data/spec/opal/core/kernel/format_spec.rb +10 -10
- data/spec/opal/core/language/predefined_spec.rb +10 -0
- data/spec/opal/core/object_id_spec.rb +56 -0
- data/spec/opal/core/runtime/bridged_classes_spec.rb +4 -4
- data/spec/opal/core/runtime_spec.rb +7 -0
- data/spec/opal/stdlib/native/native_class_spec.rb +1 -1
- data/spec/opal/stdlib/promise/always_spec.rb +30 -0
- data/spec/opal/stdlib/promise/then_spec.rb +8 -0
- data/spec/opal/stdlib/promise/trace_spec.rb +8 -0
- data/spec/rubyspecs +15 -104
- data/spec/spec_helper.rb +4 -0
- data/stdlib/native.rb +7 -18
- data/stdlib/nodejs/file.rb +1 -1
- data/stdlib/opal-parser.rb +1 -0
- data/stdlib/pp.rb +7 -5
- data/stdlib/promise.rb +53 -41
- data/tasks/testing.rake +8 -6
- metadata +28 -14
- data/spec/filters/bugs/match_data.rb +0 -13
- data/spec/filters/bugs/numeric.rb +0 -22
- data/spec/filters/bugs/regexp.rb +0 -9
- data/spec/filters/bugs/unknown.rb +0 -11
@@ -9,60 +9,12 @@ require 'opal/source_map'
|
|
9
9
|
require 'sprockets'
|
10
10
|
require 'sourcemap'
|
11
11
|
require 'erb'
|
12
|
+
require 'opal/sprockets/source_map_server'
|
13
|
+
require 'opal/sprockets/source_map_header_patch'
|
12
14
|
|
13
15
|
module Opal
|
14
|
-
|
15
|
-
class SourceMapServer
|
16
|
-
def initialize sprockets, prefix = '/'
|
17
|
-
@sprockets = sprockets
|
18
|
-
@prefix = prefix
|
19
|
-
end
|
20
|
-
|
21
|
-
attr_reader :sprockets, :prefix
|
22
|
-
|
23
|
-
def inspect
|
24
|
-
"#<#{self.class}:#{object_id}>"
|
25
|
-
end
|
26
|
-
|
27
|
-
def call(env)
|
28
|
-
prefix_regex = %r{^(?:#{prefix}/|/)}
|
29
|
-
path_info = env['PATH_INFO'].to_s.sub(prefix_regex, '')
|
30
|
-
|
31
|
-
case path_info
|
32
|
-
when %r{^(.*)\.map$}
|
33
|
-
path = $1
|
34
|
-
asset = sprockets[path]
|
35
|
-
return not_found(path) if asset.nil?
|
36
|
-
|
37
|
-
# "logical_name" of a BundledAsset keeps the .js extension
|
38
|
-
source = register[asset.logical_path.sub(/\.js$/, '')]
|
39
|
-
return not_found(asset) if source.nil?
|
40
|
-
|
41
|
-
map = JSON.parse(source)
|
42
|
-
map['sources'] = map['sources'].map {|s| "#{prefix}/#{s}"}
|
43
|
-
source = map.to_json
|
44
|
-
return not_found(asset) if source.nil?
|
45
|
-
|
46
|
-
return [200, {"Content-Type" => "text/json"}, [source.to_s]]
|
47
|
-
when %r{^(.*)\.rb$}
|
48
|
-
source = File.read(sprockets.resolve(path_info))
|
49
|
-
return not_found(path_info) if source.nil?
|
50
|
-
return [200, {"Content-Type" => "text/ruby"}, [source]]
|
51
|
-
else
|
52
|
-
not_found(path_info)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def not_found(*messages)
|
57
|
-
not_found = [404, {}, [{not_found: messages, keys: register.keys}.inspect]]
|
58
|
-
end
|
59
|
-
|
60
|
-
def register
|
61
|
-
Opal::Processor.source_map_register
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
16
|
class Server
|
17
|
+
SOURCE_MAPS_PREFIX_PATH = '/__OPAL_SOURCE_MAPS__'
|
66
18
|
|
67
19
|
attr_accessor :debug, :use_index, :index_path, :main, :public_root,
|
68
20
|
:public_urls, :sprockets, :prefix
|
@@ -113,16 +65,29 @@ module Opal
|
|
113
65
|
def create_app
|
114
66
|
server, sprockets, prefix = self, @sprockets, self.prefix
|
115
67
|
sprockets.logger.level ||= Logger::DEBUG
|
68
|
+
source_map_enabled = self.source_map_enabled
|
69
|
+
if source_map_enabled
|
70
|
+
maps_prefix = SOURCE_MAPS_PREFIX_PATH
|
71
|
+
maps_app = SourceMapServer.new(sprockets, maps_prefix)
|
72
|
+
::Opal::Sprockets::SourceMapHeaderPatch.inject!(maps_prefix)
|
73
|
+
end
|
74
|
+
|
116
75
|
@app = Rack::Builder.app do
|
117
76
|
not_found = lambda { |env| [404, {}, []] }
|
118
77
|
use Rack::Deflater
|
119
78
|
use Rack::ShowExceptions
|
120
79
|
use Index, server if server.use_index
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
80
|
+
if source_map_enabled
|
81
|
+
map(maps_prefix) do
|
82
|
+
require 'rack/conditionalget'
|
83
|
+
require 'rack/etag'
|
84
|
+
use Rack::ConditionalGet
|
85
|
+
use Rack::ETag
|
86
|
+
run maps_app
|
87
|
+
end
|
88
|
+
end
|
89
|
+
map(prefix) { run sprockets }
|
90
|
+
run Rack::Static.new(not_found, root: server.public_root, urls: server.public_urls)
|
126
91
|
end
|
127
92
|
end
|
128
93
|
|
@@ -152,37 +117,43 @@ module Opal
|
|
152
117
|
raise "index does not exist: #{@index_path}" unless File.exist?(@index_path)
|
153
118
|
Tilt.new(@index_path).render(self)
|
154
119
|
else
|
155
|
-
|
120
|
+
source
|
156
121
|
end
|
157
122
|
end
|
158
123
|
|
159
|
-
def javascript_include_tag
|
160
|
-
|
161
|
-
|
124
|
+
def javascript_include_tag name
|
125
|
+
sprockets = @server.sprockets
|
126
|
+
prefix = @server.prefix
|
127
|
+
asset = sprockets[name]
|
128
|
+
raise "Cannot find asset: #{name}" if asset.nil?
|
129
|
+
scripts = []
|
162
130
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
%Q{<script src="/assets/#{ a.logical_path }?body=1"></script>}
|
131
|
+
if @server.debug
|
132
|
+
asset.to_a.map do |dependency|
|
133
|
+
scripts << %{<script src="#{prefix}/#{dependency.logical_path}?body=1"></script>}
|
167
134
|
end
|
168
|
-
|
169
|
-
scripts.join "\n"
|
170
135
|
else
|
171
|
-
|
136
|
+
scripts << %{<script src="#{prefix}/#{name}.js"></script>}
|
172
137
|
end
|
138
|
+
|
139
|
+
scripts << %{<script>#{Opal::Processor.load_asset_code(sprockets, name)}</script>}
|
140
|
+
|
141
|
+
scripts.join "\n"
|
173
142
|
end
|
174
143
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
<
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
144
|
+
def source
|
145
|
+
<<-HTML
|
146
|
+
<!DOCTYPE html>
|
147
|
+
<html>
|
148
|
+
<head>
|
149
|
+
<title>Opal Server</title>
|
150
|
+
</head>
|
151
|
+
<body>
|
152
|
+
#{javascript_include_tag @server.main}
|
153
|
+
</body>
|
154
|
+
</html>
|
155
|
+
HTML
|
156
|
+
end
|
186
157
|
end
|
187
158
|
end
|
188
159
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'sprockets/server'
|
2
|
+
|
3
|
+
module Opal
|
4
|
+
module Sprockets
|
5
|
+
module SourceMapHeaderPatch
|
6
|
+
# Adds the source map header to all sprocket responses for assets
|
7
|
+
# with a .rb or .opal extension in the extension chain.
|
8
|
+
def headers_with_opal_source_maps(env, asset, length)
|
9
|
+
headers_without_opal_source_maps(env, asset, length).tap do |current_headers|
|
10
|
+
if asset.pathname.to_s =~ /\.(rb|opal)\b/
|
11
|
+
base_path = asset.logical_path.gsub('.js', '')
|
12
|
+
current_headers['X-SourceMap'] = "#{::Opal::Sprockets::SourceMapHeaderPatch.prefix}/#{base_path}.map"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.included(base)
|
18
|
+
# Poor man's alias_method_chain :)
|
19
|
+
base.send(:alias_method, :headers_without_opal_source_maps, :headers)
|
20
|
+
base.send(:alias_method, :headers, :headers_with_opal_source_maps)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.inject!(prefix)
|
24
|
+
self.prefix = prefix
|
25
|
+
unless ::Sprockets::Server.ancestors.include?(self)
|
26
|
+
::Sprockets::Server.send :include, self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.prefix
|
31
|
+
@prefix
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.prefix= val
|
35
|
+
@prefix = val
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Opal
|
2
|
+
class SourceMapServer
|
3
|
+
# Carelessly taken from Sprockets::Caching (Sprockets v2)
|
4
|
+
class Cache
|
5
|
+
def initialize
|
6
|
+
@cache = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :cache
|
10
|
+
|
11
|
+
def cache_get(key)
|
12
|
+
# `Cache#get(key)` for Memcache
|
13
|
+
if cache.respond_to?(:get)
|
14
|
+
cache.get(key)
|
15
|
+
|
16
|
+
# `Cache#[key]` so `Hash` can be used
|
17
|
+
elsif cache.respond_to?(:[])
|
18
|
+
cache[key]
|
19
|
+
|
20
|
+
# `Cache#read(key)` for `ActiveSupport::Cache` support
|
21
|
+
elsif cache.respond_to?(:read)
|
22
|
+
cache.read(key)
|
23
|
+
|
24
|
+
else
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def cache_set(key, value)
|
30
|
+
# `Cache#set(key, value)` for Memcache
|
31
|
+
if cache.respond_to?(:set)
|
32
|
+
cache.set(key, value)
|
33
|
+
|
34
|
+
# `Cache#[key]=value` so `Hash` can be used
|
35
|
+
elsif cache.respond_to?(:[]=)
|
36
|
+
cache[key] = value
|
37
|
+
|
38
|
+
# `Cache#write(key, value)` for `ActiveSupport::Cache` support
|
39
|
+
elsif cache.respond_to?(:write)
|
40
|
+
cache.write(key, value)
|
41
|
+
end
|
42
|
+
|
43
|
+
value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.get_map_cache(sprockets, logical_path)
|
48
|
+
logical_path = logical_path.gsub(/\.js$/, '')
|
49
|
+
cache_key = cache_key_for_path(logical_path+'.map')
|
50
|
+
cache(sprockets).cache_get(cache_key)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.set_map_cache(sprockets, logical_path, map_contents)
|
54
|
+
logical_path = logical_path.gsub(/\.js$/, '')
|
55
|
+
cache_key = cache_key_for_path(logical_path+'.map')
|
56
|
+
cache(sprockets).cache_set(cache_key, map_contents)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.cache(sprockets)
|
60
|
+
@cache ||= sprockets.cache ? sprockets : Cache.new
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.cache_key_for_path(logical_path)
|
64
|
+
base_name = logical_path.gsub(/\.js$/, '')
|
65
|
+
File.join('opal', 'source_maps', base_name)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def initialize sprockets, prefix = '/'
|
70
|
+
@sprockets = sprockets
|
71
|
+
@prefix = prefix
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_reader :sprockets, :prefix
|
75
|
+
|
76
|
+
def inspect
|
77
|
+
"#<#{self.class}:#{object_id}>"
|
78
|
+
end
|
79
|
+
|
80
|
+
def call(env)
|
81
|
+
prefix_regex = %r{^(?:#{prefix}/|/)}
|
82
|
+
path_info = env['PATH_INFO'].to_s.sub(prefix_regex, '')
|
83
|
+
|
84
|
+
case path_info
|
85
|
+
when %r{^(.*)\.map$}
|
86
|
+
path = $1
|
87
|
+
asset = sprockets[path]
|
88
|
+
return not_found(path) if asset.nil?
|
89
|
+
|
90
|
+
# "logical_name" of a BundledAsset keeps the .js extension
|
91
|
+
source = SourceMapServer.get_map_cache(sprockets, asset.logical_path)
|
92
|
+
return not_found(asset) if source.nil?
|
93
|
+
|
94
|
+
map = JSON.parse(source)
|
95
|
+
map['sources'] = map['sources'].map {|s| "#{prefix}/#{s}"}
|
96
|
+
source = map.to_json
|
97
|
+
|
98
|
+
return [200, {"Content-Type" => "text/json"}, [source.to_s]]
|
99
|
+
when %r{^(.*)\.rb$}
|
100
|
+
begin
|
101
|
+
asset = sprockets.resolve(path_info.sub(/\.rb$/,''))
|
102
|
+
rescue Sprockets::FileNotFound
|
103
|
+
return not_found(path_info)
|
104
|
+
end
|
105
|
+
return [200, {"Content-Type" => "text/ruby"}, [Pathname(asset).read]]
|
106
|
+
else
|
107
|
+
not_found(path_info)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def not_found(*messages)
|
112
|
+
not_found = [404, {}, [{not_found: messages}.inspect]]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/opal/version.rb
CHANGED
data/lib/tilt/opal.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
require 'opal/compiler'
|
3
|
+
require 'opal/config'
|
4
|
+
require 'opal/version'
|
5
|
+
|
6
|
+
$OPAL_SOURCE_MAPS = {}
|
7
|
+
|
8
|
+
module Opal
|
9
|
+
class TiltTemplate < Tilt::Template
|
10
|
+
self.default_mime_type = 'application/javascript'
|
11
|
+
|
12
|
+
def self.inherited(subclass)
|
13
|
+
subclass.default_mime_type = 'application/javascript'
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.engine_initialized?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.version
|
21
|
+
::Opal::VERSION
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.compiler_options
|
25
|
+
Opal::Config.compiler_options.merge(requirable: true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize_engine
|
29
|
+
require_template_library 'opal'
|
30
|
+
end
|
31
|
+
|
32
|
+
def prepare
|
33
|
+
end
|
34
|
+
|
35
|
+
def evaluate(context, locals, &block)
|
36
|
+
compiler_options = Opal::Config.compiler_options.merge(file: file)
|
37
|
+
compiler = Compiler.new(data, compiler_options)
|
38
|
+
compiler.compile.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def compiler_options
|
42
|
+
self.class.compiler_options
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Tilt.register 'rb', Opal::TiltTemplate
|
48
|
+
Tilt.register 'opal', Opal::TiltTemplate
|
data/opal.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
required_ruby_version = '>= 1.9.3'
|
22
22
|
|
23
23
|
s.add_dependency 'sourcemap', '~> 0.1.0'
|
24
|
-
s.add_dependency 'sprockets', '>= 2.2.3', '<
|
24
|
+
s.add_dependency 'sprockets', '>= 2.2.3', '< 4.0.0'
|
25
25
|
s.add_dependency 'hike', '~> 1.2'
|
26
26
|
s.add_dependency 'tilt', '~> 1.4'
|
27
27
|
|
data/opal/corelib/array.rb
CHANGED
@@ -228,33 +228,52 @@ class Array
|
|
228
228
|
end
|
229
229
|
|
230
230
|
def ==(other)
|
231
|
-
|
232
|
-
|
233
|
-
unless Array === other
|
234
|
-
return false unless other.respond_to? :to_ary
|
235
|
-
return other == self
|
236
|
-
end
|
231
|
+
%x{
|
232
|
+
var recursed = {};
|
237
233
|
|
238
|
-
|
234
|
+
function _eqeq(array, other) {
|
235
|
+
var i, length, a, b;
|
239
236
|
|
240
|
-
|
237
|
+
if (!other.$$is_array) {
|
238
|
+
if (#{Opal.respond_to? `other`, :to_ary}) {
|
239
|
+
return #{`other` == `array`};
|
240
|
+
} else {
|
241
|
+
return false;
|
242
|
+
}
|
243
|
+
}
|
241
244
|
|
242
|
-
|
243
|
-
for (var i = 0, length = self.length; i < length; i++) {
|
244
|
-
var a = self[i],
|
245
|
-
b = other[i];
|
245
|
+
other = #{other.to_a};
|
246
246
|
|
247
|
-
if (
|
248
|
-
|
247
|
+
if (array.length !== other.length) {
|
248
|
+
return false;
|
249
249
|
}
|
250
250
|
|
251
|
-
|
252
|
-
|
251
|
+
recursed[#{`array`.object_id}] = true;
|
252
|
+
|
253
|
+
for (i = 0, length = array.length; i < length; i++) {
|
254
|
+
a = array[i];
|
255
|
+
b = other[i];
|
256
|
+
if (a.$$is_array) {
|
257
|
+
if (b.$$is_array && b.length !== a.length) {
|
258
|
+
return false;
|
259
|
+
}
|
260
|
+
if (!recursed.hasOwnProperty(#{`a`.object_id})) {
|
261
|
+
if (!_eqeq(a, b)) {
|
262
|
+
return false;
|
263
|
+
}
|
264
|
+
}
|
265
|
+
} else {
|
266
|
+
if (!#{`a` == `b`}) {
|
267
|
+
return false;
|
268
|
+
}
|
269
|
+
}
|
253
270
|
}
|
271
|
+
|
272
|
+
return true;
|
254
273
|
}
|
255
|
-
}
|
256
274
|
|
257
|
-
|
275
|
+
return _eqeq(self, other);
|
276
|
+
}
|
258
277
|
end
|
259
278
|
|
260
279
|
def [](index, length = undefined)
|
@@ -615,7 +634,13 @@ class Array
|
|
615
634
|
}
|
616
635
|
}
|
617
636
|
|
618
|
-
|
637
|
+
if (self.length === original) {
|
638
|
+
if (#{block_given?}) {
|
639
|
+
return #{yield};
|
640
|
+
}
|
641
|
+
return nil;
|
642
|
+
}
|
643
|
+
return object;
|
619
644
|
}
|
620
645
|
end
|
621
646
|
|
@@ -709,29 +734,48 @@ class Array
|
|
709
734
|
end
|
710
735
|
|
711
736
|
def eql?(other)
|
712
|
-
|
713
|
-
|
737
|
+
%x{
|
738
|
+
var recursed = {};
|
714
739
|
|
715
|
-
|
740
|
+
function _eql(array, other) {
|
741
|
+
var i, length, a, b;
|
716
742
|
|
717
|
-
|
743
|
+
if (!other.$$is_array) {
|
744
|
+
return false;
|
745
|
+
}
|
718
746
|
|
719
|
-
|
720
|
-
for (var i = 0, length = self.length; i < length; i++) {
|
721
|
-
var a = self[i],
|
722
|
-
b = other[i];
|
747
|
+
other = #{other.to_a};
|
723
748
|
|
724
|
-
if (
|
725
|
-
|
749
|
+
if (array.length !== other.length) {
|
750
|
+
return false;
|
726
751
|
}
|
727
752
|
|
728
|
-
|
729
|
-
|
753
|
+
recursed[#{`array`.object_id}] = true;
|
754
|
+
|
755
|
+
for (i = 0, length = array.length; i < length; i++) {
|
756
|
+
a = array[i];
|
757
|
+
b = other[i];
|
758
|
+
if (a.$$is_array) {
|
759
|
+
if (b.$$is_array && b.length !== a.length) {
|
760
|
+
return false;
|
761
|
+
}
|
762
|
+
if (!recursed.hasOwnProperty(#{`a`.object_id})) {
|
763
|
+
if (!_eql(a, b)) {
|
764
|
+
return false;
|
765
|
+
}
|
766
|
+
}
|
767
|
+
} else {
|
768
|
+
if (!#{`a`.eql?(`b`)}) {
|
769
|
+
return false;
|
770
|
+
}
|
771
|
+
}
|
730
772
|
}
|
773
|
+
|
774
|
+
return true;
|
731
775
|
}
|
732
|
-
}
|
733
776
|
|
734
|
-
|
777
|
+
return _eql(self, other);
|
778
|
+
}
|
735
779
|
end
|
736
780
|
|
737
781
|
def fetch(index, defaults = undefined, &block)
|
@@ -866,30 +910,56 @@ class Array
|
|
866
910
|
|
867
911
|
def flatten(level = undefined)
|
868
912
|
%x{
|
869
|
-
var
|
913
|
+
var object_id = #{`self`.object_id};
|
870
914
|
|
871
|
-
|
872
|
-
var
|
915
|
+
function _flatten(array, level) {
|
916
|
+
var array = #{`array`.to_a},
|
917
|
+
result = [],
|
918
|
+
i, length,
|
919
|
+
item, ary;
|
873
920
|
|
874
|
-
|
875
|
-
item =
|
921
|
+
for (i = 0, length = array.length; i < length; i++) {
|
922
|
+
item = array[i];
|
876
923
|
|
877
|
-
if (
|
878
|
-
result.push
|
924
|
+
if (!#{Opal.respond_to? `item`, :to_ary}) {
|
925
|
+
result.push(item);
|
926
|
+
continue;
|
879
927
|
}
|
880
|
-
|
928
|
+
|
929
|
+
ary = #{`item`.to_ary};
|
930
|
+
|
931
|
+
if (ary === nil) {
|
881
932
|
result.push(item);
|
933
|
+
continue;
|
882
934
|
}
|
883
|
-
|
884
|
-
|
935
|
+
|
936
|
+
if (!ary.$$is_array) {
|
937
|
+
#{raise TypeError};
|
938
|
+
}
|
939
|
+
|
940
|
+
if (object_id === #{`ary`.object_id}) {
|
941
|
+
#{raise ArgumentError};
|
942
|
+
}
|
943
|
+
|
944
|
+
switch (level) {
|
945
|
+
case undefined:
|
946
|
+
result.push.apply(result, _flatten(ary));
|
947
|
+
break;
|
948
|
+
case 0:
|
949
|
+
result.push(ary);
|
950
|
+
break;
|
951
|
+
default:
|
952
|
+
result.push.apply(result, _flatten(ary, level - 1));
|
885
953
|
}
|
886
954
|
}
|
887
|
-
|
888
|
-
result.push(item);
|
889
|
-
}
|
955
|
+
return result;
|
890
956
|
}
|
891
957
|
|
892
|
-
|
958
|
+
if (level !== undefined) {
|
959
|
+
level = #{Opal.coerce_to(`level`, Integer, :to_int)};
|
960
|
+
}
|
961
|
+
|
962
|
+
return _flatten(self, level);
|
893
963
|
}
|
894
964
|
end
|
895
965
|
|
@@ -917,12 +987,15 @@ class Array
|
|
917
987
|
|
918
988
|
def hash
|
919
989
|
%x{
|
920
|
-
var hash = ['A'],
|
990
|
+
var hash = ['A'],
|
991
|
+
item;
|
921
992
|
for (var i = 0, length = self.length; i < length; i++) {
|
922
993
|
item = self[i];
|
923
|
-
|
924
|
-
|
925
|
-
|
994
|
+
if (item.$$is_array && #{`self`.eql?(`item`)}) {
|
995
|
+
hash.push('self');
|
996
|
+
} else {
|
997
|
+
hash.push(item.$hash());
|
998
|
+
}
|
926
999
|
}
|
927
1000
|
return hash.join(',');
|
928
1001
|
}
|
@@ -1022,6 +1095,7 @@ class Array
|
|
1022
1095
|
|
1023
1096
|
%x{
|
1024
1097
|
var result = [];
|
1098
|
+
var object_id = #{`self`.object_id};
|
1025
1099
|
|
1026
1100
|
for (var i = 0, length = self.length; i < length; i++) {
|
1027
1101
|
var item = self[i];
|
@@ -1039,6 +1113,10 @@ class Array
|
|
1039
1113
|
if (#{Opal.respond_to? `item`, :to_ary}) {
|
1040
1114
|
var tmp = #{`item`.to_ary};
|
1041
1115
|
|
1116
|
+
if (object_id === #{`tmp`.object_id}) {
|
1117
|
+
#{raise ArgumentError};
|
1118
|
+
}
|
1119
|
+
|
1042
1120
|
if (tmp !== nil) {
|
1043
1121
|
result.push(#{`tmp`.join(sep)});
|
1044
1122
|
|
@@ -1138,6 +1216,56 @@ class Array
|
|
1138
1216
|
end
|
1139
1217
|
end
|
1140
1218
|
|
1219
|
+
def product(*args, &block)
|
1220
|
+
%x{
|
1221
|
+
var result = #{block_given?} ? null : [],
|
1222
|
+
n = args.length + 1,
|
1223
|
+
counters = new Array(n),
|
1224
|
+
lengths = new Array(n),
|
1225
|
+
arrays = new Array(n),
|
1226
|
+
i, m, subarray, len, resultlen = 1;
|
1227
|
+
|
1228
|
+
arrays[0] = self;
|
1229
|
+
for (i = 1; i < n; i++) {
|
1230
|
+
arrays[i] = #{Opal.coerce_to(`args[i - 1]`, Array, :to_ary)};
|
1231
|
+
}
|
1232
|
+
|
1233
|
+
for (i = 0; i < n; i++) {
|
1234
|
+
len = arrays[i].length;
|
1235
|
+
if (len === 0) {
|
1236
|
+
return result || self;
|
1237
|
+
}
|
1238
|
+
resultlen *= len;
|
1239
|
+
if (resultlen > 2147483647) {
|
1240
|
+
#{raise RangeError, "too big to product"}
|
1241
|
+
}
|
1242
|
+
lengths[i] = len;
|
1243
|
+
counters[i] = 0;
|
1244
|
+
}
|
1245
|
+
|
1246
|
+
outer_loop: for (;;) {
|
1247
|
+
subarray = [];
|
1248
|
+
for (i = 0; i < n; i++) {
|
1249
|
+
subarray.push(arrays[i][counters[i]]);
|
1250
|
+
}
|
1251
|
+
if (result) {
|
1252
|
+
result.push(subarray);
|
1253
|
+
} else {
|
1254
|
+
#{yield `subarray`}
|
1255
|
+
}
|
1256
|
+
m = n - 1;
|
1257
|
+
counters[m]++;
|
1258
|
+
while (counters[m] === lengths[m]) {
|
1259
|
+
counters[m] = 0;
|
1260
|
+
if (--m < 0) break outer_loop;
|
1261
|
+
counters[m]++;
|
1262
|
+
}
|
1263
|
+
}
|
1264
|
+
|
1265
|
+
return result || self;
|
1266
|
+
}
|
1267
|
+
end
|
1268
|
+
|
1141
1269
|
def push(*objects)
|
1142
1270
|
%x{
|
1143
1271
|
for (var i = 0, length = objects.length; i < length; i++) {
|