opal 0.8.1 → 0.9.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -2
- data/.gitmodules +3 -3
- data/.jshintrc +17 -20
- data/.travis.yml +22 -11
- data/CHANGELOG.md +51 -1
- data/CODE_OF_CONDUCT.md +15 -0
- data/CONTRIBUTING.md +125 -9
- data/Gemfile +1 -1
- data/Guardfile +2 -2
- data/README.md +95 -29
- data/Rakefile +1 -1
- data/benchmark/benchmarks +103 -0
- data/benchmark/bm_array_flatten.rb +9 -0
- data/benchmark/bm_array_intersection_numbers.rb +7 -0
- data/benchmark/bm_array_intersection_objects.rb +7 -0
- data/benchmark/bm_array_intersection_strings.rb +7 -0
- data/benchmark/bm_array_join_ary.rb +9 -0
- data/benchmark/bm_array_minus_numbers.rb +7 -0
- data/benchmark/bm_array_minus_objects.rb +7 -0
- data/benchmark/bm_array_minus_strings.rb +7 -0
- data/benchmark/bm_array_union_numbers.rb +7 -0
- data/benchmark/bm_array_union_objects.rb +7 -0
- data/benchmark/bm_array_union_strings.rb +7 -0
- data/benchmark/bm_array_uniq_bang_numbers.rb +5 -0
- data/benchmark/bm_array_uniq_bang_objects.rb +5 -0
- data/benchmark/bm_array_uniq_bang_strings.rb +5 -0
- data/benchmark/bm_array_uniq_numbers.rb +5 -0
- data/benchmark/bm_array_uniq_objects.rb +5 -0
- data/benchmark/bm_array_uniq_strings.rb +5 -0
- data/benchmark/bm_dispatch_bind_table.rb +57 -0
- data/benchmark/bm_dispatch_code_gen.rb +65 -0
- data/benchmark/bm_dispatch_code_gen_if.rb +64 -0
- data/benchmark/bm_dispatch_hardcoded.rb +44 -0
- data/benchmark/bm_dispatch_send.rb +38 -0
- data/benchmark/bm_dispatch_send_table.rb +57 -0
- data/benchmark/bm_hash_assoc_object.rb +11 -0
- data/benchmark/bm_hash_assoc_string.rb +9 -0
- data/benchmark/bm_hash_clone_object.rb +9 -0
- data/benchmark/bm_hash_clone_string.rb +9 -0
- data/benchmark/bm_hash_delete_object.rb +11 -0
- data/benchmark/bm_hash_delete_string.rb +9 -0
- data/benchmark/bm_hash_each_key_object.rb +9 -0
- data/benchmark/bm_hash_each_key_string.rb +9 -0
- data/benchmark/bm_hash_each_object.rb +9 -0
- data/benchmark/bm_hash_each_string.rb +9 -0
- data/benchmark/bm_hash_each_value_object.rb +9 -0
- data/benchmark/bm_hash_each_value_string.rb +9 -0
- data/benchmark/bm_hash_element_reference_object.rb +11 -0
- data/benchmark/bm_hash_element_reference_string.rb +9 -0
- data/benchmark/bm_hash_element_set_object.rb +5 -0
- data/benchmark/bm_hash_element_set_string.rb +5 -0
- data/benchmark/bm_hash_equal_value_object.rb +14 -0
- data/benchmark/bm_hash_equal_value_string.rb +11 -0
- data/benchmark/bm_hash_fetch_object.rb +11 -0
- data/benchmark/bm_hash_fetch_string.rb +9 -0
- data/benchmark/bm_hash_flatten_object.rb +9 -0
- data/benchmark/bm_hash_flatten_string.rb +9 -0
- data/benchmark/bm_hash_has_key_object.rb +11 -0
- data/benchmark/bm_hash_has_key_string.rb +9 -0
- data/benchmark/bm_hash_has_value_object.rb +9 -0
- data/benchmark/bm_hash_has_value_string.rb +9 -0
- data/benchmark/bm_hash_hash_object.rb +9 -0
- data/benchmark/bm_hash_hash_string.rb +9 -0
- data/benchmark/bm_hash_inspect_object.rb +9 -0
- data/benchmark/bm_hash_inspect_string.rb +9 -0
- data/benchmark/bm_hash_invert_object.rb +9 -0
- data/benchmark/bm_hash_invert_string.rb +9 -0
- data/benchmark/bm_hash_keep_if_object.rb +9 -0
- data/benchmark/bm_hash_keep_if_string.rb +9 -0
- data/benchmark/bm_hash_key_object.rb +9 -0
- data/benchmark/bm_hash_key_string.rb +9 -0
- data/benchmark/bm_hash_keys_object.rb +9 -0
- data/benchmark/bm_hash_keys_string.rb +9 -0
- data/benchmark/bm_hash_literal_mixed_large.rb +3 -0
- data/benchmark/bm_hash_literal_mixed_small.rb +3 -0
- data/benchmark/bm_hash_literal_object_large.rb +4 -0
- data/benchmark/bm_hash_literal_object_small.rb +3 -0
- data/benchmark/bm_hash_literal_string_large.rb +4 -0
- data/benchmark/bm_hash_literal_string_small.rb +3 -0
- data/benchmark/bm_hash_merge_object.rb +22 -0
- data/benchmark/bm_hash_merge_string.rb +18 -0
- data/benchmark/bm_hash_rassoc_object.rb +9 -0
- data/benchmark/bm_hash_rassoc_string.rb +9 -0
- data/benchmark/bm_hash_rehash_object.rb +9 -0
- data/benchmark/bm_hash_rehash_string.rb +9 -0
- data/benchmark/bm_hash_reject_bang_object.rb +9 -0
- data/benchmark/bm_hash_reject_bang_string.rb +9 -0
- data/benchmark/bm_hash_reject_object.rb +9 -0
- data/benchmark/bm_hash_reject_string.rb +9 -0
- data/benchmark/bm_hash_replace_object.rb +18 -0
- data/benchmark/bm_hash_replace_string.rb +14 -0
- data/benchmark/bm_hash_select_bang_object.rb +9 -0
- data/benchmark/bm_hash_select_bang_string.rb +9 -0
- data/benchmark/bm_hash_select_object.rb +9 -0
- data/benchmark/bm_hash_select_string.rb +9 -0
- data/benchmark/bm_hash_shift_object.rb +10 -0
- data/benchmark/bm_hash_shift_string.rb +10 -0
- data/benchmark/bm_hash_to_a_object.rb +9 -0
- data/benchmark/bm_hash_to_a_string.rb +9 -0
- data/benchmark/bm_hash_to_h_object.rb +10 -0
- data/benchmark/bm_hash_to_h_string.rb +10 -0
- data/benchmark/bm_hash_values_object.rb +9 -0
- data/benchmark/bm_hash_values_string.rb +9 -0
- data/benchmark/run.rb +48 -0
- data/bin/opal-mspec +1 -1
- data/bin/opal-repl +4 -4
- data/docs/compiled_ruby.md +214 -56
- data/docs/configuring_gems.md +2 -2
- data/docs/faq.md +2 -2
- data/docs/getting_started.md +19 -2
- data/docs/jquery.md +5 -5
- data/docs/opal_parser.md +53 -0
- data/docs/unsupported_features.md +2 -2
- data/docs/upgrading.md +22 -0
- data/docs/using_sprockets.md +15 -0
- data/examples/rack/config.ru +13 -0
- data/examples/sinatra/config.ru +4 -5
- data/lib/mspec/opal/runner.rb +54 -11
- data/lib/opal.rb +1 -1
- data/lib/opal/builder.rb +1 -1
- data/lib/opal/builder_processors.rb +1 -1
- data/lib/opal/cli.rb +17 -13
- data/lib/opal/cli_options.rb +1 -1
- data/lib/opal/compiler.rb +12 -0
- data/lib/opal/config.rb +4 -0
- data/lib/opal/nodes/arglist.rb +5 -7
- data/lib/opal/nodes/call.rb +6 -1
- data/lib/opal/nodes/call_special.rb +74 -0
- data/lib/opal/nodes/def.rb +35 -28
- data/lib/opal/nodes/definitions.rb +3 -5
- data/lib/opal/nodes/for.rb +13 -0
- data/lib/opal/nodes/helpers.rb +15 -1
- data/lib/opal/nodes/if.rb +5 -5
- data/lib/opal/nodes/iter.rb +6 -1
- data/lib/opal/nodes/literal.rb +1 -1
- data/lib/opal/nodes/logic.rb +2 -2
- data/lib/opal/nodes/masgn.rb +1 -2
- data/lib/opal/nodes/module.rb +2 -1
- data/lib/opal/nodes/rescue.rb +10 -1
- data/lib/opal/nodes/scope.rb +8 -2
- data/lib/opal/nodes/singleton_class.rb +1 -1
- data/lib/opal/nodes/top.rb +11 -0
- data/lib/opal/nodes/variables.rb +4 -4
- data/lib/opal/parser.rb +21 -3
- data/lib/opal/parser/grammar.rb +3115 -2961
- data/lib/opal/parser/grammar.y +29 -6
- data/lib/opal/parser/lexer.rb +18 -8
- data/lib/opal/sprockets.rb +85 -0
- data/lib/opal/sprockets/processor.rb +11 -35
- data/lib/opal/sprockets/server.rb +3 -15
- data/lib/opal/version.rb +2 -2
- data/opal.gemspec +4 -4
- data/opal/README.md +9 -0
- data/opal/corelib/array.rb +433 -181
- data/opal/corelib/basic_object.rb +48 -4
- data/opal/corelib/boolean.rb +15 -6
- data/opal/corelib/class.rb +6 -5
- data/opal/corelib/comparable.rb +12 -0
- data/opal/corelib/complex.rb +282 -0
- data/opal/corelib/constants.rb +9 -0
- data/opal/corelib/enumerable.rb +83 -34
- data/opal/corelib/enumerator.rb +3 -1
- data/opal/corelib/error.rb +49 -10
- data/opal/corelib/file.rb +1 -0
- data/opal/corelib/hash.rb +353 -577
- data/opal/corelib/helpers.rb +20 -0
- data/opal/corelib/kernel.rb +114 -59
- data/opal/corelib/math.rb +470 -0
- data/opal/corelib/method.rb +11 -2
- data/opal/corelib/module.rb +96 -96
- data/opal/corelib/{nil_class.rb → nil.rb} +20 -1
- data/opal/corelib/number.rb +751 -0
- data/opal/corelib/numeric.rb +77 -437
- data/opal/corelib/proc.rb +81 -1
- data/opal/corelib/process.rb +27 -0
- data/opal/corelib/rational.rb +358 -0
- data/opal/corelib/regexp.rb +156 -27
- data/opal/corelib/runtime.js +724 -335
- data/opal/corelib/string.rb +93 -104
- data/opal/corelib/string/encoding.rb +177 -0
- data/opal/corelib/string/inheritance.rb +2 -0
- data/opal/corelib/struct.rb +105 -18
- data/opal/corelib/time.rb +267 -146
- data/opal/corelib/unsupported.rb +216 -0
- data/opal/corelib/variables.rb +0 -6
- data/opal/opal.rb +8 -22
- data/opal/opal/base.rb +9 -0
- data/opal/opal/mini.rb +17 -0
- data/spec/README.md +1 -1
- data/spec/filters/bugs/array.rb +38 -136
- data/spec/filters/bugs/{basic_object.rb → basicobject.rb} +14 -15
- data/spec/filters/bugs/class.rb +6 -12
- data/spec/filters/bugs/complex.rb +3 -0
- data/spec/filters/bugs/date.rb +162 -10
- data/spec/filters/bugs/enumerable.rb +31 -58
- data/spec/filters/bugs/enumerator.rb +42 -0
- data/spec/filters/bugs/exception.rb +66 -10
- data/spec/filters/bugs/float.rb +17 -0
- data/spec/filters/bugs/hash.rb +11 -97
- data/spec/filters/bugs/inheritance.rb +5 -0
- data/spec/filters/bugs/integer.rb +28 -0
- data/spec/filters/bugs/kernel.rb +304 -12
- data/spec/filters/bugs/language.rb +133 -399
- data/spec/filters/bugs/language_opal.rb +88 -0
- data/spec/filters/bugs/module.rb +203 -62
- data/spec/filters/bugs/numeric.rb +32 -0
- data/spec/filters/bugs/proc.rb +39 -0
- data/spec/filters/bugs/range.rb +148 -0
- data/spec/filters/bugs/regexp.rb +168 -0
- data/spec/filters/bugs/set.rb +46 -3
- data/spec/filters/bugs/singleton.rb +1 -2
- data/spec/filters/bugs/string.rb +59 -90
- data/spec/filters/bugs/strscan.rb +80 -0
- data/spec/filters/bugs/struct.rb +10 -20
- data/spec/filters/bugs/time.rb +17 -184
- data/spec/filters/bugs/unboundmethod.rb +22 -0
- data/spec/filters/unsupported/array.rb +163 -0
- data/spec/filters/unsupported/basicobject.rb +14 -0
- data/spec/filters/unsupported/bignum.rb +46 -0
- data/spec/filters/unsupported/class.rb +4 -0
- data/spec/filters/unsupported/delegator.rb +5 -0
- data/spec/filters/unsupported/enumerable.rb +11 -0
- data/spec/filters/unsupported/enumerator.rb +8 -9
- data/spec/filters/unsupported/fixnum.rb +14 -0
- data/spec/filters/unsupported/float.rb +41 -7
- data/spec/filters/unsupported/freeze.rb +45 -0
- data/spec/filters/unsupported/hash.rb +50 -0
- data/spec/filters/unsupported/integer.rb +3 -0
- data/spec/filters/unsupported/kernel.rb +31 -0
- data/spec/filters/unsupported/language.rb +17 -0
- data/spec/filters/unsupported/matchdata.rb +30 -0
- data/spec/filters/unsupported/math.rb +3 -0
- data/spec/filters/unsupported/module.rb +5 -3
- data/spec/filters/unsupported/pathname.rb +3 -0
- data/spec/filters/unsupported/privacy.rb +136 -0
- data/spec/filters/unsupported/proc.rb +3 -0
- data/spec/filters/unsupported/regexp.rb +59 -0
- data/spec/filters/unsupported/set.rb +4 -0
- data/spec/filters/unsupported/{marshal.rb → singleton.rb} +4 -2
- data/spec/filters/unsupported/{mutable_strings.rb → string.rb} +456 -336
- data/spec/filters/unsupported/struct.rb +3 -0
- data/spec/filters/unsupported/symbol.rb +5 -0
- data/spec/filters/unsupported/taint.rb +16 -0
- data/spec/filters/unsupported/thread.rb +5 -0
- data/spec/filters/unsupported/time.rb +197 -16
- data/spec/lib/cli_spec.rb +14 -4
- data/spec/lib/compiler_spec.rb +9 -1
- data/spec/lib/parser/call_spec.rb +18 -0
- data/spec/lib/parser/not_spec.rb +2 -8
- data/spec/lib/sprockets_spec.rb +24 -0
- data/spec/opal/core/array/intersection_spec.rb +38 -0
- data/spec/opal/core/array/minus_spec.rb +38 -0
- data/spec/opal/core/array/union_spec.rb +38 -0
- data/spec/opal/core/array/uniq_spec.rb +49 -0
- data/spec/opal/core/exception_spec.rb +7 -0
- data/spec/opal/core/fixtures/require_tree_with_dot/file 1.rb +1 -0
- data/spec/opal/core/fixtures/require_tree_with_dot/file 2.rb +1 -0
- data/spec/opal/core/fixtures/require_tree_with_dot/file 3.rb +1 -0
- data/spec/opal/core/fixtures/require_tree_with_dot/index.rb +3 -0
- data/spec/opal/core/hash/internals_spec.rb +332 -0
- data/spec/opal/core/helpers_spec.rb +14 -0
- data/spec/opal/core/kernel/freeze_spec.rb +1 -1
- data/spec/opal/core/kernel/raise_spec.rb +13 -0
- data/spec/opal/core/kernel/require_tree_spec.rb +9 -0
- data/spec/opal/core/language/class_spec.rb +55 -0
- data/spec/opal/core/language/fixtures/send.rb +1 -0
- data/spec/opal/core/language/keyword_arguments_spec.rb +11 -0
- data/spec/opal/core/language/send_spec.rb +5 -0
- data/spec/opal/core/method/to_proc_spec.rb +28 -0
- data/spec/opal/core/module/name_spec.rb +0 -17
- data/spec/opal/core/runtime/bridged_classes_spec.rb +2 -2
- data/spec/opal/core/runtime/eval_spec.rb +1 -1
- data/spec/opal/core/runtime/method_missing_spec.rb +6 -0
- data/spec/opal/core/runtime_spec.rb +51 -0
- data/spec/opal/stdlib/js_spec.rb +66 -0
- data/spec/opal/stdlib/native/hash_spec.rb +36 -0
- data/spec/rubyspecs +152 -273
- data/spec/spec_helper.rb +10 -11
- data/stdlib/base64.rb +9 -9
- data/stdlib/benchmark.rb +551 -4
- data/stdlib/console.rb +94 -0
- data/stdlib/date.rb +1 -1
- data/stdlib/encoding.rb +1 -170
- data/stdlib/js.rb +56 -0
- data/stdlib/json.rb +9 -14
- data/stdlib/math.rb +1 -370
- data/stdlib/native.rb +133 -63
- data/stdlib/nodejs/file.rb +5 -0
- data/stdlib/nodejs/fileutils.rb +13 -6
- data/stdlib/nodejs/node_modules/js-yaml/node_modules/argparse/README.md +1 -1
- data/stdlib/opal-parser.rb +1 -2
- data/stdlib/ostruct.rb +65 -6
- data/stdlib/pp.rb +2 -4
- data/stdlib/rbconfig.rb +1 -3
- data/stdlib/strscan.rb +164 -28
- data/tasks/benchmarking.rake +88 -0
- data/tasks/testing.rake +181 -55
- data/{lib/mspec/opal/special_calls.rb → tasks/testing/mspec_special_calls.rb} +1 -1
- data/{lib/mspec/opal/sprockets.js → tasks/testing/phantomjs1-sprockets.js} +17 -6
- data/test/opal/test_keyword.rb +590 -0
- data/vendored-minitest/minitest.rb +2 -2
- data/vendored-minitest/test/unit.rb +5 -0
- metadata +229 -62
- data/benchmarks/operators.rb +0 -11
- data/benchmarks/prova.js.rb +0 -13
- data/docs/libraries.md +0 -36
- data/lib/mspec/opal/new.html.erb +0 -1
- data/lib/mspec/opal/rake_task.rb +0 -248
- data/opal/corelib/match_data.rb +0 -128
- data/spec/filters/bugs/math.rb +0 -95
- data/spec/filters/bugs/nil.rb +0 -7
- data/spec/filters/bugs/opal.rb +0 -9
- data/spec/filters/bugs/regular_expressions.rb +0 -41
- data/spec/filters/bugs/stringscanner.rb +0 -33
- data/spec/filters/unsupported/encoding.rb +0 -102
- data/spec/filters/unsupported/frozen.rb +0 -92
- data/spec/filters/unsupported/hash_compare_by_identity.rb +0 -16
- data/spec/filters/unsupported/integer_size.rb +0 -59
- data/spec/filters/unsupported/method_added.rb +0 -10
- data/spec/filters/unsupported/private_constants.rb +0 -30
- data/spec/filters/unsupported/private_methods.rb +0 -55
- data/spec/filters/unsupported/random.rb +0 -4
- data/spec/filters/unsupported/rational_numbers.rb +0 -4
- data/spec/filters/unsupported/regular_expressions.rb +0 -137
- data/spec/filters/unsupported/ruby_exe.rb +0 -5
- data/spec/filters/unsupported/symbols.rb +0 -17
- data/spec/filters/unsupported/tainted.rb +0 -180
- data/spec/filters/unsupported/trusted.rb +0 -88
- data/stdlib/process.rb +0 -10
- data/tasks/documenting.rake +0 -37
data/benchmark/run.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
if RUBY_ENGINE == 'opal'
|
2
|
+
require 'opal/compiler'
|
3
|
+
require 'nodejs'
|
4
|
+
end
|
5
|
+
|
6
|
+
BEST_OF_N = Integer(ENV['BEST_OF_N']) rescue 1
|
7
|
+
|
8
|
+
require 'benchmark'
|
9
|
+
|
10
|
+
files = ARGV
|
11
|
+
|
12
|
+
if files.empty?
|
13
|
+
files = File.read('benchmark/benchmarks').lines.map(&:strip).reject do |line|
|
14
|
+
line.empty? || line.start_with?('#')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
maxlen = files.max_by{|file| file.length}.length + 1
|
19
|
+
|
20
|
+
total_time = 0
|
21
|
+
|
22
|
+
files.each do |file|
|
23
|
+
print file, " " * (maxlen - file.length)
|
24
|
+
|
25
|
+
times = []
|
26
|
+
|
27
|
+
if RUBY_ENGINE == 'opal'
|
28
|
+
code = Opal.compile(File.read(file))
|
29
|
+
BEST_OF_N.times do
|
30
|
+
times << Benchmark.measure { `eval(code)` }
|
31
|
+
end
|
32
|
+
else
|
33
|
+
code = File.read(file)
|
34
|
+
BEST_OF_N.times do
|
35
|
+
times << Benchmark.measure { eval(code) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
time = times.min_by{|t| t.real}
|
40
|
+
|
41
|
+
total_time += time.real
|
42
|
+
|
43
|
+
print time.real, "\n"
|
44
|
+
end
|
45
|
+
|
46
|
+
bottom_line = "Executed #{ files.length } benchmark#{ 's' if files.length != 1} in #{ total_time } sec"
|
47
|
+
$stderr.print "=" * bottom_line.length, "\n"
|
48
|
+
$stderr.print bottom_line, "\n"
|
data/bin/opal-mspec
CHANGED
@@ -7,4 +7,4 @@ pattern = %Q{MSPEC_PATTERN="{#{specs.join(',')}}"} if specs.any?
|
|
7
7
|
command = [pattern, 'rake mspec'].compact.join(' ')
|
8
8
|
exec command
|
9
9
|
|
10
|
-
# RUBYOPT="-rbundler/setup -rmspec/opal/special_calls" bundle exec mspec run -t opal -pspec/
|
10
|
+
# RUBYOPT="-rbundler/setup -rmspec/opal/special_calls" bundle exec mspec run -t opal -pspec/rubyspec/spec_helper spec/rubyspec/core/true/*
|
data/bin/opal-repl
CHANGED
@@ -14,7 +14,7 @@ module Opal
|
|
14
14
|
begin
|
15
15
|
require 'v8'
|
16
16
|
rescue LoadError
|
17
|
-
abort 'therubyracer
|
17
|
+
abort 'opal-repl depends on therubyracer gem, which is not currently installed'
|
18
18
|
end
|
19
19
|
|
20
20
|
@v8 = V8::Context.new
|
@@ -41,11 +41,11 @@ module Opal
|
|
41
41
|
|
42
42
|
loop do
|
43
43
|
# on SIGINT lets just return from the loop..
|
44
|
-
trap(
|
44
|
+
trap('SIGINT') { finish; return }
|
45
45
|
line = Readline.readline '>> ', true
|
46
46
|
|
47
47
|
# if we type exit, then we need to close down context
|
48
|
-
if line ==
|
48
|
+
if line == 'exit' or line.nil?
|
49
49
|
break
|
50
50
|
end
|
51
51
|
|
@@ -57,7 +57,7 @@ module Opal
|
|
57
57
|
|
58
58
|
def eval_ruby(str)
|
59
59
|
code = Opal::Builder.new.build_str(str, '(irb)', :irb => true, :const_missing => true)
|
60
|
-
code.processed[0...-1].each{ |c| @v8.eval(c.to_s) }
|
60
|
+
code.processed[0...-1].each { |c| @v8.eval(c.to_s) }
|
61
61
|
@v8.eval "var $_result = #{code.processed.last.to_s} ($_result == null ? 'nil' : $_result.$inspect());"
|
62
62
|
rescue => e
|
63
63
|
puts "#{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
data/docs/compiled_ruby.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Compiled Ruby Code
|
2
2
|
|
3
|
-
## Generated
|
3
|
+
## Generated JavaScript
|
4
4
|
|
5
5
|
Opal is a source-to-source compiler, so there is no VM as such and the
|
6
6
|
compiled code aims to be as fast and efficient as possible, mapping
|
@@ -43,11 +43,11 @@ Hello there.
|
|
43
43
|
EOS
|
44
44
|
```
|
45
45
|
|
46
|
-
Ruby strings are compiled directly into
|
46
|
+
Ruby strings are compiled directly into JavaScript strings for
|
47
47
|
performance as well as readability. This has the side effect that Opal
|
48
48
|
does not support mutable strings - i.e. all strings are immutable.
|
49
49
|
|
50
|
-
NOTE: Strings in Opal are immutable because they are compiled into regular
|
50
|
+
NOTE: Strings in Opal are immutable because they are compiled into regular JavaScript strings. This is done for performance reasons.
|
51
51
|
|
52
52
|
For performance reasons, symbols are also compiled directly into strings.
|
53
53
|
Opal supports all the symbol syntaxes, but does not have a real `Symbol`
|
@@ -55,8 +55,8 @@ class. Symbols and Strings can therefore be used interchangeably.
|
|
55
55
|
|
56
56
|
#### Numbers
|
57
57
|
|
58
|
-
In Opal there is a single class for numbers; `Numeric`. To keep
|
59
|
-
as performant as possible,
|
58
|
+
In Opal there is a single class for numbers; `Numeric`. To keep Opal
|
59
|
+
as performant as possible, Ruby numbers are mapped to native numbers.
|
60
60
|
This has the side effect that all numbers must be of the same class.
|
61
61
|
Most relevant methods from `Integer`, `Float` and `Numeric` are
|
62
62
|
implemented on this class.
|
@@ -68,8 +68,8 @@ implemented on this class.
|
|
68
68
|
|
69
69
|
#### Arrays
|
70
70
|
|
71
|
-
Ruby arrays are compiled directly into
|
72
|
-
|
71
|
+
Ruby arrays are compiled directly into JavaScript arrays. Special
|
72
|
+
Ruby syntaxes for word arrays etc are also supported.
|
73
73
|
|
74
74
|
```ruby
|
75
75
|
[1, 2, 3, 4] # => [1, 2, 3, 4]
|
@@ -78,30 +78,30 @@ ruby syntaxes for word arrays etc are also supported.
|
|
78
78
|
|
79
79
|
#### Hash
|
80
80
|
|
81
|
-
Inside a generated
|
82
|
-
creates a new hash. This is also available in
|
81
|
+
Inside a generated Ruby script, a function `Opal.hash` is available which
|
82
|
+
creates a new hash. This is also available in JavaScript as `Opal.hash`
|
83
83
|
and simply returns a new instance of the `Hash` class.
|
84
84
|
|
85
85
|
```ruby
|
86
|
-
{ :foo => 100, :baz => 700 } # =>
|
87
|
-
{ foo: 42, bar: [1, 2, 3] } # =>
|
86
|
+
{ :foo => 100, :baz => 700 } # => Opal.hash("foo", 100, "baz", 700)
|
87
|
+
{ foo: 42, bar: [1, 2, 3] } # => Opal.hash("foo", 42, "bar", [1, 2, 3])
|
88
88
|
```
|
89
89
|
|
90
90
|
#### Range
|
91
91
|
|
92
|
-
Similar to hash, there is a function `
|
92
|
+
Similar to hash, there is a function `Opal.range` available to create
|
93
93
|
range instances.
|
94
94
|
|
95
95
|
```ruby
|
96
|
-
1..4 # =>
|
97
|
-
3...7 # =>
|
96
|
+
1..4 # => Opal.range(1, 4, true)
|
97
|
+
3...7 # => Opal.range(3, 7, false)
|
98
98
|
```
|
99
99
|
|
100
100
|
### Logic and conditionals
|
101
101
|
|
102
|
-
As per
|
102
|
+
As per Ruby, Opal treats only `false` and `nil` as falsy, everything
|
103
103
|
else is a truthy value including `""`, `0` and `[]`. This differs from
|
104
|
-
|
104
|
+
JavaScript as these values are also treated as false.
|
105
105
|
|
106
106
|
For this reason, most truthy tests must check if values are `false` or
|
107
107
|
`nil`.
|
@@ -133,7 +133,7 @@ This makes the generated truthy tests (`if` statements, `and` checks and
|
|
133
133
|
|
134
134
|
Instance variables in Opal work just as expected. When ivars are set or
|
135
135
|
retrieved on an object, they are set natively without the `@` prefix.
|
136
|
-
This allows real
|
136
|
+
This allows real JavaScript identifiers to be used which is more
|
137
137
|
efficient then accessing variables by string name.
|
138
138
|
|
139
139
|
```ruby
|
@@ -152,13 +152,13 @@ this.foo; // => 200
|
|
152
152
|
this.bar; // => nil
|
153
153
|
```
|
154
154
|
|
155
|
-
NOTE: If an instance variable uses the same name as a reserved
|
155
|
+
NOTE: If an instance variable uses the same name as a reserved JavaScript keyword,
|
156
156
|
then the instance variable is wrapped using the object-key notation: `this['class']`.
|
157
157
|
|
158
158
|
## Compiled Files
|
159
159
|
|
160
|
-
As described above, a compiled
|
161
|
-
of
|
160
|
+
As described above, a compiled Ruby source gets generated into a string
|
161
|
+
of JavaScript code that is wrapped inside an anonymous function. This
|
162
162
|
looks similar to the following:
|
163
163
|
|
164
164
|
```javascript
|
@@ -194,14 +194,14 @@ written to the browser's console.
|
|
194
194
|
|
195
195
|
### Debugging and finding errors
|
196
196
|
|
197
|
-
Because Opal does not aim to be fully compatible with
|
197
|
+
Because Opal does not aim to be fully compatible with Ruby, there are
|
198
198
|
some instances where things can break and it may not be entirely
|
199
199
|
obvious what went wrong.
|
200
200
|
|
201
|
-
### Using
|
201
|
+
### Using JavaScript debuggers
|
202
202
|
|
203
|
-
As
|
204
|
-
debugger to work through
|
203
|
+
As Opal just generates JavaScript, it is useful to use a native
|
204
|
+
debugger to work through JavaScript code. To use a debugger, simply
|
205
205
|
add an x-string similar to the following at the place you wish to
|
206
206
|
debug:
|
207
207
|
|
@@ -211,31 +211,32 @@ debug:
|
|
211
211
|
# .. more code
|
212
212
|
```
|
213
213
|
The x-strings just pass the debugger statement straight through to the
|
214
|
-
|
214
|
+
JavaScript output.
|
215
215
|
|
216
|
-
NOTE: All local variables and method/block arguments also keep their
|
217
|
-
names except in the rare cases when the name is reserved in
|
216
|
+
NOTE: All local variables and method/block arguments also keep their Ruby
|
217
|
+
names except in the rare cases when the name is reserved in JavaScript.
|
218
218
|
In these cases, a `$` suffix is added to the name
|
219
219
|
(e.g. `try` → `try$`).
|
220
220
|
|
221
|
-
## Javascript from Ruby
|
222
221
|
|
223
|
-
|
222
|
+
## JavaScript from Ruby
|
223
|
+
|
224
|
+
Opal tries to interact as cleanly with JavaScript and its api as much
|
224
225
|
as possible. Ruby arrays, strings, numbers, regexps, blocks and booleans
|
225
|
-
are just
|
226
|
+
are just JavaScript native equivalents. The only boxed core features are
|
226
227
|
hashes.
|
227
228
|
|
228
229
|
|
229
|
-
### Inline
|
230
|
+
### Inline JavaScript
|
230
231
|
|
231
|
-
As most of the corelib deals with these low level details,
|
232
|
-
a special syntax for inlining
|
233
|
-
x-strings or "backticks", as their
|
232
|
+
As most of the corelib deals with these low level details, Opal provides
|
233
|
+
a special syntax for inlining JavaScript code. This is done with
|
234
|
+
x-strings or "backticks", as their Ruby use has no useful translation
|
234
235
|
in the browser.
|
235
236
|
|
236
237
|
```ruby
|
237
238
|
`window.title`
|
238
|
-
# => "Opal: Ruby to
|
239
|
+
# => "Opal: Ruby to JavaScript compiler"
|
239
240
|
|
240
241
|
%x{
|
241
242
|
console.log("opal version is:");
|
@@ -267,7 +268,9 @@ as used by this example.
|
|
267
268
|
|
268
269
|
_Reposted from: [Mikamayhem](http://dev.mikamai.com/post/79398725537/using-native-javascript-objects-from-opal)_
|
269
270
|
|
270
|
-
Opal standard lib (stdlib) includes a `Native` module,
|
271
|
+
Opal standard lib (stdlib) includes a `Native` module. To use it, you need to download and reference `native.js`. You can find the latest minified one from the CDN [here](http://cdn.opalrb.org/opal/current/native.min.js).
|
272
|
+
|
273
|
+
Let's see how it works and wrap `window`:
|
271
274
|
|
272
275
|
```ruby
|
273
276
|
require 'native'
|
@@ -314,34 +317,190 @@ That’s all for now, bye!
|
|
314
317
|
window.close!
|
315
318
|
```
|
316
319
|
|
317
|
-
|
320
|
+
### Calling JavaScript Methods
|
321
|
+
|
322
|
+
You can make direct JavaScript method calls on using the `recv.JS.method`
|
323
|
+
syntax. For example, if you have a JavaScript object named `foo` and want to call the
|
324
|
+
`bar` method on it with no arguments, with or without parentheses:
|
325
|
+
|
326
|
+
```ruby
|
327
|
+
# javascript: foo.bar()
|
328
|
+
foo.JS.bar
|
329
|
+
foo.JS.bar()
|
330
|
+
```
|
331
|
+
|
332
|
+
You can call the JavaScript methods with arguments, with or without parentheses, just
|
333
|
+
like Ruby methods:
|
334
|
+
|
335
|
+
```ruby
|
336
|
+
# JavaScript: foo.bar(1, "a")
|
337
|
+
foo.JS.bar(1, :a)
|
338
|
+
foo.JS.bar 1, :a
|
339
|
+
```
|
340
|
+
|
341
|
+
You can call the JavaScript methods with argument splats:
|
342
|
+
|
343
|
+
```ruby
|
344
|
+
# JavaScript: ($a = foo).bar.apply($a, [1].concat([2, 3]))
|
345
|
+
foo.JS.bar(1, *[2, 3])
|
346
|
+
foo.JS.bar 1, *[2, 3]
|
347
|
+
```
|
348
|
+
|
349
|
+
You can provide a block when making a JavaScript method call, and it will be
|
350
|
+
converted to a JavaScript function added as the last argument to the method:
|
351
|
+
|
352
|
+
```ruby
|
353
|
+
# JavaScript:
|
354
|
+
# ($a = (TMP_1 = function(arg){
|
355
|
+
# var self = TMP_1.$$s || this;
|
356
|
+
# if (arg == null) arg = nil;
|
357
|
+
# return "" + (arg.method()) + " " + (self.$baz(3))
|
358
|
+
# },
|
359
|
+
# TMP_1.$$s = self, TMP_1),
|
360
|
+
# foo.bar)(1, 2, $a);
|
361
|
+
foo.JS.bar(1, 2){|arg| arg.JS.method + baz(3)}
|
362
|
+
```
|
363
|
+
|
364
|
+
Note how `self` is set for the JavaScript function passed as an argument. This
|
365
|
+
allows normal Ruby block behavior to work when passing blocks to JavaScript
|
366
|
+
methods.
|
318
367
|
|
319
|
-
|
368
|
+
The `.JS.` syntax is recognized as a special token by the lexer, so if you have
|
369
|
+
a Ruby method named `JS` that you want to call, you can add a space to call it:
|
370
|
+
|
371
|
+
```ruby
|
372
|
+
# call Ruby JS method on foo, call Ruby bar method on result
|
373
|
+
foo. JS.bar
|
374
|
+
```
|
375
|
+
|
376
|
+
### Getting/Setting JavaScript Properties
|
377
|
+
|
378
|
+
You can get JavaScript properties using the `recv.JS[:property]` syntax:
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
# JavaScript: foo["bar"]
|
382
|
+
foo.JS[:bar]
|
383
|
+
```
|
384
|
+
|
385
|
+
This also works for JavaScript array access:
|
386
|
+
|
387
|
+
```ruby
|
388
|
+
# JavaScript: foo[2]
|
389
|
+
foo.JS[2]
|
390
|
+
```
|
391
|
+
|
392
|
+
You can set JavaScript properties using this as the left hand side in an
|
393
|
+
assignment:
|
394
|
+
|
395
|
+
```ruby
|
396
|
+
# JavaScript: foo["bar"] = 1
|
397
|
+
foo.JS[:bar] = 1
|
398
|
+
```
|
399
|
+
|
400
|
+
This also works for setting values in a JavaScript array:
|
401
|
+
|
402
|
+
```ruby
|
403
|
+
# JavaScript: foo[2] = "a"
|
404
|
+
foo.JS[2] = :a
|
405
|
+
```
|
406
|
+
|
407
|
+
Like the `recv.JS.method` syntax, `.JS[` is recognized as a special token by
|
408
|
+
the lexer, so if you want to call the Ruby `JS` method on a object and then
|
409
|
+
call the Ruby `[]` method on the result, you can add a space:
|
410
|
+
|
411
|
+
```ruby
|
412
|
+
# call Ruby JS method on foo, call Ruby [] method on result with :a argument
|
413
|
+
foo. JS[:a]
|
414
|
+
```
|
415
|
+
|
416
|
+
### Calling JavaScript Operators
|
417
|
+
|
418
|
+
Opal has a `js` library in the stdlib that provides a `JS` module which can
|
419
|
+
be used to call JavaScript operators such as `new`. Example:
|
420
|
+
|
421
|
+
```ruby
|
422
|
+
require 'js'
|
423
|
+
|
424
|
+
# new foo(bar)
|
425
|
+
JS.new(foo, bar)
|
426
|
+
|
427
|
+
# delete foo["bar"]
|
428
|
+
JS.delete(foo, :bar)
|
429
|
+
|
430
|
+
# "bar" in foo
|
431
|
+
JS.in(:bar, foo)
|
432
|
+
|
433
|
+
# foo instanceof bar
|
434
|
+
JS.instanceof(foo, bar)
|
435
|
+
|
436
|
+
# typeof foo
|
437
|
+
JS.typeof(foo)
|
438
|
+
```
|
439
|
+
|
440
|
+
### Calling JavaScript Global Functions
|
441
|
+
|
442
|
+
You can also use the `js` library to call JavaScript global functions via
|
443
|
+
`JS.call`:
|
444
|
+
|
445
|
+
```ruby
|
446
|
+
require 'js'
|
447
|
+
|
448
|
+
# parseFloat("1.1")
|
449
|
+
JS.call(:parseFloat, "1.1")
|
450
|
+
```
|
451
|
+
|
452
|
+
For convenience, `method_missing` is aliased to call, allowing you to call
|
453
|
+
global JavaScript methods directly on the `JS` module:
|
454
|
+
|
455
|
+
```ruby
|
456
|
+
require 'js'
|
457
|
+
|
458
|
+
# parseFloat("1.1")
|
459
|
+
JS.parseFloat("1.1")
|
460
|
+
```
|
461
|
+
|
462
|
+
|
463
|
+
### Wrapping JavaScript Libraries
|
464
|
+
|
465
|
+
If you want to integrate a JavaScript library with Opal, so that you can make Ruby calls, you can choose one of the following options:
|
466
|
+
|
467
|
+
- **Use backticks:** This is the quickest, simplest approach to integrating: call the native JavaScript code directly; it may provide a slight performance benefit, but also produces "ugly" Ruby code riddled with JavaScript. It's ideal for occasional calls to a JavaScript library.
|
468
|
+
|
469
|
+
- **Use `.JS`:** You can make direct JavaScript method calls on using the `recv.JS.method` syntax. It is very similar to using backticks but looks more like ruby.
|
470
|
+
|
471
|
+
- **Use `Native`:** `Native` provides a reasonable Ruby-like wrapper around JavaScript objects. This provides a quick in-term solution if no dedicated Ruby wrapper library exists.
|
472
|
+
|
473
|
+
- **Create your own Wrapper Library:** If you use the library a lot, you can create your own Ruby library that wraps the JavaScript calls (which call `Native` or use backticks under the hood). This provides the best abstraction (eg. you can provide high-level calls that provide functionality, regardless of if the underlying JavaScript call flows change).
|
474
|
+
|
475
|
+
|
476
|
+
## Ruby from JavaScript
|
477
|
+
|
478
|
+
Accessing classes and methods defined in Opal from the JavaScript runtime is
|
320
479
|
possible via the `Opal` js object. The following class:
|
321
480
|
|
322
481
|
```ruby
|
323
482
|
class Foo
|
324
483
|
def bar
|
325
|
-
puts "called bar on class Foo defined in
|
484
|
+
puts "called bar on class Foo defined in Ruby code"
|
326
485
|
end
|
327
486
|
end
|
328
487
|
```
|
329
488
|
|
330
|
-
Can be accessed from
|
489
|
+
Can be accessed from JavaScript like this:
|
331
490
|
|
332
491
|
```javascript
|
333
492
|
Opal.Foo.$new().$bar();
|
334
|
-
// => "called bar on class Foo defined in
|
493
|
+
// => "called bar on class Foo defined in Ruby code"
|
335
494
|
```
|
336
495
|
|
337
|
-
Remember that all
|
496
|
+
Remember that all Ruby methods are prefixed with a `$`.
|
338
497
|
|
339
|
-
In the case that a method name can't be called directly due to a
|
498
|
+
In the case that a method name can't be called directly due to a JavaScript syntax error, you will need to call the method using bracket notation. For example, you can call `foo.$merge(...)` but not `foo.$merge!(...)`, `bar.$fetch('somekey')` but not `bar.$[]('somekey')`. Instead you would write it like this: `foo['$merge!'](...)` or `bar['$[]']('somekey')`.
|
340
499
|
|
341
500
|
|
342
501
|
### Hash
|
343
502
|
|
344
|
-
Since
|
503
|
+
Since Ruby hashes are implemented directly with an Opal class, there's no "toll-free" bridging available (unlike with strings and arrays, for example). However, it's quite possible to interact with hashes from JavaScript:
|
345
504
|
|
346
505
|
```javascript
|
347
506
|
var myHash = Opal.hash({a: 1, b: 2});
|
@@ -355,7 +514,7 @@ myHash.$fetch('z','');
|
|
355
514
|
myHash.$update(Opal.hash({b: 20, c: 30}));
|
356
515
|
// output of $inspect: {"a"=>10, "b"=>20, "c"=>30}
|
357
516
|
myHash.$to_n(); // provided by the Native module
|
358
|
-
// output: {"a": 10, "b": 20, "c": 30} aka a standard
|
517
|
+
// output: {"a": 10, "b": 20, "c": 30} aka a standard JavaScript object
|
359
518
|
```
|
360
519
|
|
361
520
|
NOTE: Be aware `Hash#to_n` produces a duplicate copy of the hash.
|
@@ -364,32 +523,32 @@ NOTE: Be aware `Hash#to_n` produces a duplicate copy of the hash.
|
|
364
523
|
|
365
524
|
### Method Missing
|
366
525
|
|
367
|
-
Opal supports `method_missing`. This is a key feature of
|
526
|
+
Opal supports `method_missing`. This is a key feature of Ruby, and Opal wouldn't be much use without it! This page details the implementation of `method_missing` for Opal.
|
368
527
|
|
369
528
|
#### Method dispatches
|
370
529
|
|
371
|
-
Firstly, a
|
530
|
+
Firstly, a Ruby call `foo.bar 1, 2, 3` is compiled into the following JavaScript:
|
372
531
|
|
373
532
|
```javascript
|
374
533
|
foo.$bar(1, 2, 3)
|
375
534
|
```
|
376
535
|
|
377
|
-
This should be pretty easy to read. The `bar` method has a `$` prefix just to distinguish it from underlying
|
536
|
+
This should be pretty easy to read. The `bar` method has a `$` prefix just to distinguish it from underlying JavaScript properties, as well as Ruby ivars. Methods are compiled like this to make the generated code really readable.
|
378
537
|
|
379
538
|
#### Handling `method_missing`
|
380
539
|
|
381
|
-
|
540
|
+
JavaScript does not have an equivalent of `method_missing`, so how do we handle it? If a function is missing in JavaScript, then a language level exception will be raised.
|
382
541
|
|
383
|
-
To get around this, we make use of our compiler. During parsing, we collect a list of all method calls made inside a
|
542
|
+
To get around this, we make use of our compiler. During parsing, we collect a list of all method calls made inside a Ruby file, and this gives us a list of all possible method calls. We then add stub methods to the root object prototype (an Opal object, not the global JavaScript Object) which will proxy our method missing calls for us.
|
384
543
|
|
385
|
-
For example, assume the following
|
544
|
+
For example, assume the following Ruby script:
|
386
545
|
|
387
546
|
```ruby
|
388
547
|
first 1, 2, 3
|
389
548
|
second "wow".to_sym
|
390
549
|
```
|
391
550
|
|
392
|
-
After parsing, we know we only ever call 3 methods: `[:first, :second, :to_sym]`. So, imagine we could just add these 3 methods to `BasicObject` in
|
551
|
+
After parsing, we know we only ever call 3 methods: `[:first, :second, :to_sym]`. So, imagine we could just add these 3 methods to `BasicObject` in Ruby, we would get something like this:
|
393
552
|
|
394
553
|
```ruby
|
395
554
|
class BasicObject
|
@@ -407,19 +566,18 @@ class BasicObject
|
|
407
566
|
end
|
408
567
|
```
|
409
568
|
|
410
|
-
It is obvious from here, that unless an object defines any given method, it will always resort in a dispatch to `method_missing` from one of our defined stub methods. This is how we get `method_missing` in
|
569
|
+
It is obvious from here, that unless an object defines any given method, it will always resort in a dispatch to `method_missing` from one of our defined stub methods. This is how we get `method_missing` in Opal.
|
411
570
|
|
412
571
|
#### Optimising generated code
|
413
572
|
|
414
|
-
To optimise the generated code slightly, we reduce the code output from the compiler into the following
|
573
|
+
To optimise the generated code slightly, we reduce the code output from the compiler into the following JavaScript:
|
415
574
|
|
416
575
|
```javascript
|
417
576
|
Opal.add_stubs(["first", "second", "to_sym"]);
|
418
577
|
```
|
419
578
|
|
420
|
-
You will see this at the top of all your generated
|
579
|
+
You will see this at the top of all your generated JavaScript files. This will add a stub method for all methods used in your file.
|
421
580
|
|
422
581
|
#### Alternative approaches
|
423
582
|
|
424
583
|
The old approach was to inline `method_missing` calls by checking for a method on **every method dispatch**. This is still supported via a parser option, but not recommended.
|
425
|
-
|