opal 0.3.18 → 0.3.19
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 +1 -1
- data/Gemfile +1 -3
- data/README.md +472 -10
- data/Rakefile +10 -52
- data/core/array.rb +9 -14
- data/core/basic_object.rb +7 -10
- data/core/boolean.rb +5 -1
- data/core/class.rb +15 -38
- data/core/dir.rb +89 -0
- data/core/enumerable.rb +133 -57
- data/core/error.rb +15 -1
- data/core/file.rb +85 -0
- data/core/hash.rb +186 -32
- data/core/kernel.rb +30 -31
- data/core/load_order +4 -2
- data/core/module.rb +42 -62
- data/core/numeric.rb +7 -1
- data/core/object.rb +1 -1
- data/core/proc.rb +6 -2
- data/core/range.rb +16 -28
- data/core/regexp.rb +3 -3
- data/core/runtime.js +281 -350
- data/core/string.rb +100 -110
- data/docs/CNAME +1 -0
- data/docs/Rakefile +55 -0
- data/docs/css/styles.css +50 -0
- data/docs/css/syntax.css +63 -0
- data/docs/layout/post.html +3 -0
- data/docs/layout/pre.html +11 -0
- data/examples/dependencies/app.rb +3 -0
- data/lib/opal.rb +2 -1
- data/lib/opal/builder.rb +36 -10
- data/lib/opal/builder_task.rb +51 -24
- data/lib/opal/grammar.rb +2509 -2439
- data/lib/opal/grammar.y +38 -5
- data/lib/opal/lexer.rb +18 -2
- data/lib/opal/parser.rb +375 -349
- data/lib/opal/scope.rb +24 -2
- data/lib/opal/version.rb +1 -1
- data/spec/builder/build_order_spec.rb +20 -0
- data/spec/builder/lib_name_for_spec.rb +24 -0
- data/spec/grammar/call_spec.rb +9 -6
- data/spec/grammar/lambda_spec.rb +64 -0
- data/spec/grammar/sclass_spec.rb +5 -3
- data/{core/spec → test}/core/array/allocate_spec.rb +0 -0
- data/{core/spec → test}/core/array/append_spec.rb +0 -0
- data/{core/spec → test}/core/array/assoc_spec.rb +0 -0
- data/{core/spec → test}/core/array/at_spec.rb +0 -0
- data/{core/spec → test}/core/array/clear_spec.rb +0 -0
- data/{core/spec → test}/core/array/clone_spec.rb +0 -0
- data/{core/spec → test}/core/array/collect_spec.rb +0 -0
- data/{core/spec → test}/core/array/compact_spec.rb +0 -0
- data/{core/spec → test}/core/array/concat_spec.rb +0 -0
- data/{core/spec → test}/core/array/constructor_spec.rb +0 -0
- data/{core/spec → test}/core/array/count_spec.rb +0 -0
- data/{core/spec → test}/core/array/delete_at_spec.rb +0 -0
- data/{core/spec → test}/core/array/delete_if_spec.rb +0 -0
- data/{core/spec → test}/core/array/delete_spec.rb +0 -0
- data/{core/spec → test}/core/array/each_index_spec.rb +0 -0
- data/{core/spec → test}/core/array/each_spec.rb +0 -0
- data/{core/spec → test}/core/array/element_reference_spec.rb +0 -0
- data/{core/spec → test}/core/array/empty_spec.rb +0 -0
- data/{core/spec → test}/core/array/eql_spec.rb +0 -0
- data/{core/spec → test}/core/array/fetch_spec.rb +0 -0
- data/{core/spec → test}/core/array/first_spec.rb +0 -0
- data/{core/spec → test}/core/array/flatten_spec.rb +0 -0
- data/{core/spec → test}/core/array/include_spec.rb +0 -0
- data/{core/spec → test}/core/array/insert_spec.rb +0 -0
- data/{core/spec → test}/core/array/last_spec.rb +0 -0
- data/{core/spec → test}/core/array/length_spec.rb +0 -0
- data/{core/spec → test}/core/array/map_spec.rb +0 -0
- data/{core/spec → test}/core/array/plus_spec.rb +0 -0
- data/{core/spec → test}/core/array/pop_spec.rb +0 -0
- data/{core/spec → test}/core/array/push_spec.rb +0 -0
- data/{core/spec → test}/core/array/rassoc_spec.rb +0 -0
- data/{core/spec → test}/core/array/reject_spec.rb +0 -0
- data/{core/spec → test}/core/array/replace_spec.rb +0 -0
- data/{core/spec → test}/core/array/reverse_each_spec.rb +0 -0
- data/{core/spec → test}/core/array/reverse_spec.rb +0 -0
- data/{core/spec → test}/core/array/size_spec.rb +0 -0
- data/{core/spec → test}/core/array/to_ary_spec.rb +0 -0
- data/{core/spec → test}/core/array/uniq_spec.rb +0 -0
- data/{core/spec → test}/core/array/zip_spec.rb +0 -0
- data/test/core/class/fixtures/classes.rb +9 -0
- data/test/core/class/new_spec.rb +108 -0
- data/{core/spec → test}/core/enumerable/all_spec.rb +0 -0
- data/{core/spec → test}/core/enumerable/any_spec.rb +0 -0
- data/{core/spec → test}/core/enumerable/collect_spec.rb +0 -0
- data/{core/spec → test}/core/enumerable/count_spec.rb +0 -0
- data/test/core/enumerable/detect_spec.rb +48 -0
- data/test/core/enumerable/drop_spec.rb +17 -0
- data/test/core/enumerable/drop_while_spec.rb +24 -0
- data/test/core/enumerable/each_with_index_spec.rb +11 -0
- data/test/core/enumerable/each_with_object_spec.rb +17 -0
- data/test/core/enumerable/entries_spec.rb +6 -0
- data/test/core/enumerable/find_all_spec.rb +13 -0
- data/test/core/enumerable/find_index_spec.rb +45 -0
- data/test/core/enumerable/find_spec.rb +48 -0
- data/test/core/enumerable/first_spec.rb +40 -0
- data/{core/spec → test}/core/enumerable/fixtures/classes.rb +19 -0
- data/test/core/enumerable/grep_spec.rb +21 -0
- data/test/core/enumerable/take_spec.rb +40 -0
- data/test/core/enumerable/to_a_spec.rb +6 -0
- data/{core/spec → test}/core/false/and_spec.rb +0 -0
- data/{core/spec → test}/core/false/inspect_spec.rb +0 -0
- data/{core/spec → test}/core/false/or_spec.rb +0 -0
- data/{core/spec → test}/core/false/to_s_spec.rb +0 -0
- data/{core/spec → test}/core/false/xor_spec.rb +0 -0
- data/test/core/file/expand_path_spec.rb +20 -0
- data/{core/spec → test}/core/hash/allocate_spec.rb +0 -0
- data/{core/spec → test}/core/hash/assoc_spec.rb +0 -0
- data/{core/spec → test}/core/hash/clear_spec.rb +0 -0
- data/{core/spec → test}/core/hash/clone_spec.rb +0 -0
- data/test/core/hash/default_spec.rb +9 -0
- data/{core/spec → test}/core/hash/delete_if_spec.rb +0 -0
- data/test/core/hash/each_key_spec.rb +15 -0
- data/test/core/hash/each_pair_spec.rb +30 -0
- data/test/core/hash/each_spec.rb +30 -0
- data/test/core/hash/each_value_spec.rb +15 -0
- data/{core/spec → test}/core/hash/element_reference_spec.rb +14 -0
- data/{core/spec → test}/core/hash/element_set_spec.rb +1 -0
- data/test/core/hash/empty_spec.rb +10 -0
- data/test/core/hash/fetch_spec.rb +24 -0
- data/test/core/hash/flatten_spec.rb +46 -0
- data/test/core/hash/has_key_spec.rb +24 -0
- data/test/core/hash/has_value_spec.rb +12 -0
- data/test/core/hash/include_spec.rb +24 -0
- data/test/core/hash/index_spec.rb +13 -0
- data/test/core/hash/indexes_spec.rb +9 -0
- data/test/core/hash/indices_spec.rb +9 -0
- data/test/core/hash/invert_spec.rb +12 -0
- data/test/core/hash/keep_if_spec.rb +18 -0
- data/test/core/hash/key_spec.rb +24 -0
- data/test/core/hash/keys_spec.rb +10 -0
- data/test/core/hash/length_spec.rb +10 -0
- data/test/core/hash/member_spec.rb +24 -0
- data/{core/spec → test}/core/hash/merge_spec.rb +0 -0
- data/{core/spec → test}/core/hash/new_spec.rb +0 -0
- data/test/core/hash/rassoc_spec.rb +34 -0
- data/test/core/hash/replace_spec.rb +7 -0
- data/test/core/hash/select_spec.rb +52 -0
- data/test/core/hash/shift_spec.rb +19 -0
- data/test/core/hash/size_spec.rb +10 -0
- data/test/core/hash/update_spec.rb +17 -0
- data/test/core/hash/value_spec.rb +12 -0
- data/test/core/hash/values_at_spec.rb +9 -0
- data/test/core/hash/values_spec.rb +7 -0
- data/test/core/kernel/eql_spec.rb +15 -0
- data/test/core/kernel/equal_value_spec.rb +12 -0
- data/test/core/kernel/loop_spec.rb +23 -0
- data/test/core/kernel/nil_spec.rb +7 -0
- data/test/core/kernel/proc_spec.rb +9 -0
- data/test/core/kernel/rand_spec.rb +14 -0
- data/test/core/kernel/respond_to_spec.rb +24 -0
- data/test/core/kernel/send_spec.rb +56 -0
- data/test/core/kernel/tap_spec.rb +10 -0
- data/test/core/kernel/to_s_spec.rb +5 -0
- data/{core/spec → test}/core/matchdata/to_a_spec.rb +0 -0
- data/{core/spec → test}/core/nil/and_spec.rb +0 -0
- data/{core/spec → test}/core/nil/inspect_spec.rb +0 -0
- data/{core/spec → test}/core/nil/nil_spec.rb +0 -0
- data/{core/spec → test}/core/nil/or_spec.rb +0 -0
- data/{core/spec → test}/core/nil/to_a_spec.rb +0 -0
- data/{core/spec → test}/core/nil/to_f_spec.rb +0 -0
- data/{core/spec → test}/core/nil/to_i_spec.rb +0 -0
- data/{core/spec → test}/core/nil/to_s_spec.rb +0 -0
- data/{core/spec → test}/core/nil/xor_spec.rb +0 -0
- data/{core/spec → test}/core/numeric/equal_value_spec.rb +0 -0
- data/test/core/range/begin_spec.rb +9 -0
- data/test/core/range/case_compare_spec.rb +16 -0
- data/test/core/range/end_spec.rb +9 -0
- data/{core/spec → test}/core/regexp/match_spec.rb +0 -0
- data/test/core/string/capitalize_spec.rb +10 -0
- data/test/core/string/casecmp_spec.rb +16 -0
- data/test/core/string/chomp_spec.rb +43 -0
- data/test/core/string/chop_spec.rb +10 -0
- data/test/core/string/chr_spec.rb +13 -0
- data/test/core/string/comparison_spec.rb +13 -0
- data/test/core/string/downcase_spec.rb +6 -0
- data/test/core/string/element_reference_spec.rb +72 -0
- data/test/core/string/empty_spec.rb +8 -0
- data/test/core/string/end_with_spec.rb +12 -0
- data/test/core/string/fixtures/classes.rb +3 -0
- data/test/core/string/gsub_spec.rb +17 -0
- data/test/core/string/include_spec.rb +12 -0
- data/test/core/string/intern_spec.rb +9 -0
- data/test/core/string/length_spec.rb +9 -0
- data/test/core/string/lstrip_spec.rb +7 -0
- data/test/core/string/match_spec.rb +27 -0
- data/test/core/string/next_spec.rb +10 -0
- data/test/core/string/ord_spec.rb +9 -0
- data/test/core/string/partition_spec.rb +10 -0
- data/test/core/string/reverse_spec.rb +7 -0
- data/test/core/string/rstrip_spec.rb +7 -0
- data/test/core/string/size_spec.rb +9 -0
- data/test/core/string/slice_spec.rb +72 -0
- data/test/core/string/split_spec.rb +5 -0
- data/test/core/string/start_with_spec.rb +12 -0
- data/test/core/string/strip_spec.rb +6 -0
- data/test/core/string/sub_spec.rb +22 -0
- data/test/core/string/succ_spec.rb +10 -0
- data/test/core/string/sum_spec.rb +5 -0
- data/test/core/string/swapcase_spec.rb +18 -0
- data/test/core/string/to_a_spec.rb +9 -0
- data/test/core/string/to_f_spec.rb +14 -0
- data/test/core/string/to_i_spec.rb +25 -0
- data/test/core/string/to_s_spec.rb +13 -0
- data/test/core/string/to_str_spec.rb +13 -0
- data/test/core/string/to_sym_spec.rb +9 -0
- data/test/core/string/upcase_spec.rb +6 -0
- data/test/core/symbol/to_proc_spec.rb +12 -0
- data/{core/spec → test}/core/true/and_spec.rb +0 -0
- data/{core/spec → test}/core/true/inspect_spec.rb +0 -0
- data/{core/spec → test}/core/true/or_spec.rb +0 -0
- data/{core/spec → test}/core/true/to_s_spec.rb +0 -0
- data/{core/spec → test}/core/true/xor_spec.rb +0 -0
- data/test/index.html +11 -0
- data/{core/spec → test}/language/alias_spec.rb +4 -0
- data/{core/spec → test}/language/and_spec.rb +0 -0
- data/{core/spec → test}/language/array_spec.rb +0 -0
- data/{core/spec → test}/language/block_spec.rb +0 -0
- data/{core/spec → test}/language/break_spec.rb +0 -0
- data/{core/spec → test}/language/case_spec.rb +0 -0
- data/{core/spec → test}/language/defined_spec.rb +0 -0
- data/{core/spec → test}/language/ensure_spec.rb +0 -0
- data/test/language/fixtures/yield.rb +23 -0
- data/{core/spec → test}/language/hash_spec.rb +0 -0
- data/{core/spec → test}/language/if_spec.rb +0 -0
- data/test/language/literal_lambda_spec.rb +47 -0
- data/{core/spec → test}/language/loop_spec.rb +0 -0
- data/{core/spec → test}/language/metaclass_spec.rb +0 -0
- data/{core/spec → test}/language/next_spec.rb +0 -0
- data/{core/spec → test}/language/or_spec.rb +0 -0
- data/{core/spec → test}/language/predefined_spec.rb +0 -0
- data/{core/spec → test}/language/regexp_spec.rb +0 -0
- data/{core/spec → test}/language/send_spec.rb +0 -0
- data/{core/spec → test}/language/singleton_class_spec.rb +0 -0
- data/{core/spec → test}/language/super_spec.rb +0 -0
- data/{core/spec → test}/language/symbol_spec.rb +0 -0
- data/{core/spec → test}/language/undef_spec.rb +0 -0
- data/{core/spec → test}/language/unless_spec.rb +0 -0
- data/{core/spec → test}/language/until_spec.rb +0 -0
- data/{core/spec → test}/language/variables_spec.rb +0 -0
- data/{core/spec → test}/language/while_spec.rb +0 -0
- data/test/language/yield_spec.rb +100 -0
- data/test/opal/array/subclassing_spec.rb +32 -0
- data/test/opal/class/bridge_class_spec.rb +37 -0
- data/test/opal/exception/subclassing_spec.rb +17 -0
- data/test/opal/runtime/_methods_spec.rb +48 -0
- data/test/opal/runtime/class_hierarchy_spec.rb +22 -0
- data/test/opal/runtime/def_spec.rb +23 -0
- data/test/opal/string/subclassing_spec.rb +26 -0
- data/test/spec_helper.rb +3 -0
- metadata +437 -111
- data/core/spec/core/class/new_spec.rb +0 -16
- data/core/spec/core/hash/default_spec.rb +0 -4
- data/core/spec/core/symbol/to_proc_spec.rb +0 -6
- data/core/spec/index.html +0 -11
- data/spec/builder/build_source_spec.rb +0 -52
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,25 +1,487 @@
|
|
1
1
|
# Opal
|
2
2
|
|
3
|
-
Opal is a ruby to javascript compiler
|
3
|
+
**Opal is a ruby to javascript compiler.** Opal aims to take ruby files
|
4
|
+
and generate efficient javascript that maintains rubys features. Opal
|
5
|
+
will, by default, generate fast and efficient code in preference to
|
6
|
+
keeping all ruby features.
|
4
7
|
|
5
|
-
|
8
|
+
Opal comes with an implementation of the ruby corelib, written in ruby,
|
9
|
+
that uses a bundled runtime (written in javascript) that tie all the
|
10
|
+
features together. Whenever possible Opal bridges to native javascript
|
11
|
+
features under the hood. The Opal gem includes the compiler used to
|
12
|
+
convert ruby sources into javascript.
|
6
13
|
|
7
|
-
|
14
|
+
Opal is [hosted on github](http://github.com/adambeynon/opal), and there
|
15
|
+
is a Freenode IRC channel at `#opal`.
|
8
16
|
|
9
|
-
##
|
17
|
+
## Downloads
|
10
18
|
|
11
|
-
|
19
|
+
The Opal runtime and corelib are distributed here, and are required to
|
20
|
+
run any code generated by opal.
|
21
|
+
|
22
|
+
[Opal version 0.3.19](http://opalrb.org/opal.js) _(14.9kb Minified And Gzipped)_
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
Opal comes distributed as a gem, so either install with:
|
12
27
|
|
13
28
|
gem install opal
|
14
29
|
|
15
|
-
Or
|
30
|
+
Or add to your Gemfile:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
gem "opal"
|
34
|
+
```
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
To quickly compile ruby code into javascript, use the `Opal.parse()`
|
39
|
+
method which returns a string of javascript code:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
require 'opal'
|
43
|
+
|
44
|
+
Opal.parse("[1, 2, 3, 4].each { |a| puts a }")
|
45
|
+
```
|
46
|
+
|
47
|
+
This will return a string of javascript similar to the following:
|
48
|
+
|
49
|
+
```javascript
|
50
|
+
(function() {
|
51
|
+
// compiled ruby
|
52
|
+
}).call(Opal.top);
|
53
|
+
```
|
54
|
+
|
55
|
+
This can then be written to a file and run in any browser.
|
56
|
+
|
57
|
+
### Creating a rake task
|
58
|
+
|
59
|
+
Using a Rakefile makes it simple to build your application code.
|
60
|
+
Assuming your code is in a file `app.rb`, add a rake task:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
# Rakefile
|
64
|
+
|
65
|
+
require 'opal'
|
66
|
+
|
67
|
+
desc "Build opal application"
|
68
|
+
task :build do
|
69
|
+
src = File.read 'app.rb'
|
70
|
+
js = Opal.parse src
|
71
|
+
|
72
|
+
File.open('app.js', 'w+') do |out|
|
73
|
+
out.write js
|
74
|
+
end
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
Running `rake build` will then read your app code, compile it and then
|
79
|
+
write it out to a file ready to load in a web browser.
|
80
|
+
|
81
|
+
### Setting up html file
|
82
|
+
|
83
|
+
The generated `app.js` file can just be added into any HTML page. The
|
84
|
+
opal runtime needs to be loaded first (you can download that above).
|
85
|
+
|
86
|
+
```html
|
87
|
+
<!doctype html>
|
88
|
+
<html>
|
89
|
+
<head>
|
90
|
+
<title>Test Opal App</title>
|
91
|
+
</head>
|
92
|
+
<body>
|
93
|
+
<script src="opal.js"></script>
|
94
|
+
<script src="app.js"></script>
|
95
|
+
</body>
|
96
|
+
</html>
|
97
|
+
```
|
98
|
+
|
99
|
+
When using `Opal.parse()` as above, the generated code will be run
|
100
|
+
as soon as the page loads. Open the browsers console and you should
|
101
|
+
see the 4 numbers printed to the console.
|
102
|
+
|
103
|
+
## Builder and Dependency Builder
|
104
|
+
|
105
|
+
The previous example was useful for building very simple apps using
|
106
|
+
opal. For more complex apps with dependencies, Opal provides useful
|
107
|
+
rake tasks to get started. Assuming opal is in your lib path, create
|
108
|
+
a `Rakefile` similar to:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
# Rakefile
|
112
|
+
|
113
|
+
require 'opal'
|
114
|
+
|
115
|
+
Opal::BuilderTask.new do |t|
|
116
|
+
t.name = 'my-first-app'
|
117
|
+
t.dependencies = ['opal-json']
|
118
|
+
t.files = ['app.rb']
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
This simple rake task is all you need to build an app with its
|
123
|
+
dependencies.
|
124
|
+
|
125
|
+
### Building dependencies
|
126
|
+
|
127
|
+
To build the opal runtime `opal.js`, as well as `opal-json` into
|
128
|
+
`build/`, run the simple rake task:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
rake dependencies
|
132
|
+
```
|
133
|
+
|
134
|
+
This will try and find the `opal-json` gem installed, so either
|
135
|
+
install globally with `gem install opal-json`, or add it to your
|
136
|
+
Gemfile as `gem "opal-json"` and run `bundle install`.
|
137
|
+
|
138
|
+
### Building app
|
139
|
+
|
140
|
+
To build the listed files into your application, run:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
rake build
|
144
|
+
```
|
145
|
+
|
146
|
+
This will build to `/build/my-first-app.js`. To customize the output
|
147
|
+
filename, change the `name` property in the raketask.
|
148
|
+
|
149
|
+
### Running the app
|
150
|
+
|
151
|
+
You should now be able to run the built app using a standard HTML page.
|
152
|
+
|
153
|
+
```html
|
154
|
+
<!doctype html>
|
155
|
+
<html>
|
156
|
+
<head>
|
157
|
+
<title>Test Opal App</title>
|
158
|
+
</head>
|
159
|
+
<body>
|
160
|
+
<script src="build/opal.js"></script>
|
161
|
+
<script src="build/opal-json.js"></script>
|
162
|
+
<script src="build/my-first-app.js"></script>
|
163
|
+
</body>
|
164
|
+
</html>
|
165
|
+
```
|
166
|
+
|
167
|
+
### Main file
|
168
|
+
|
169
|
+
When using the `BuilderTask`, the files generated will not be run
|
170
|
+
automatically on page load. The files are registered so they can be
|
171
|
+
loaded using `require()`. Builder is clever enough though that it will,
|
172
|
+
by default, automatically require the first file in the app to be
|
173
|
+
loaded. This will appear at the bottom of `my-first-app.js`:
|
174
|
+
|
175
|
+
```javascript
|
176
|
+
Opal.define('app', function() {
|
177
|
+
// app.rb code
|
178
|
+
});
|
179
|
+
|
180
|
+
Opal.require('app');
|
181
|
+
```
|
182
|
+
|
183
|
+
## Features And Implementation
|
184
|
+
|
185
|
+
Opal is a source-to-source compiler, so there is no VM as such and the
|
186
|
+
compiled code aims to be as fast and efficient as possible, mapping
|
187
|
+
directly to underlying javascript features and objects where possible.
|
188
|
+
|
189
|
+
### Literals
|
190
|
+
|
191
|
+
**self** is always compiled to `this`. Any context inside the generated
|
192
|
+
code is usually a function body; whether it be a method body, a block,
|
193
|
+
a class/module body or the file itself.
|
194
|
+
|
195
|
+
**true** and **false** are compiled directly into their native boolean
|
196
|
+
equivalents. This makes interaction a lot easier as there is no need
|
197
|
+
to convert values to opal specific values. It does mean that there is
|
198
|
+
only a `Boolean` ruby class available, not seperate `TrueClass` and
|
199
|
+
`FalseClass` classes.
|
200
|
+
|
201
|
+
**nil** is compiled into a `nil` reference, which inside all generated
|
202
|
+
files points to a special object which is just an instance of the ruby
|
203
|
+
`NilClass` class. This object is available externally to javascript as
|
204
|
+
`Opal.nil`.
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
nil # => nil
|
208
|
+
true # => true
|
209
|
+
false # => false
|
210
|
+
self # => this
|
211
|
+
```
|
212
|
+
|
213
|
+
#### Strings
|
214
|
+
|
215
|
+
Ruby strings are compiled directly into javascript strings for
|
216
|
+
performance as well as readability. This has the side effect that Opal
|
217
|
+
does not support mutable strings - i.e. all strings are immutable.
|
218
|
+
|
219
|
+
#### Symbols
|
220
|
+
|
221
|
+
For performance reasons, symbols compile directly into strings. Opal
|
222
|
+
supports all the symbol syntaxes, but does not have a real `Symbol`
|
223
|
+
class. Symbols and Strings can therefore be used interchangeably.
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
"hello world!" # => "hello world!"
|
227
|
+
:foo # => "foo"
|
228
|
+
<<-EOS # => "\nHello there.\n"
|
229
|
+
Hello there.
|
230
|
+
EOS
|
231
|
+
```
|
232
|
+
|
233
|
+
#### Numbers
|
234
|
+
|
235
|
+
In Opal there is a single class for numbers; `Numeric`. To keep opal
|
236
|
+
as performant as possible, ruby numbers are mapped to native numbers.
|
237
|
+
This has the side effect that all numbers must be of the same class.
|
238
|
+
Most relevant methods from `Integer`, `Float` and `Numeric` are
|
239
|
+
implemented on this class.
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
42 # => 42
|
243
|
+
3.142 # => 3.142
|
244
|
+
```
|
245
|
+
|
246
|
+
#### Arrays
|
247
|
+
|
248
|
+
Ruby arrays are compiled directly into javascript arrays. Special
|
249
|
+
ruby syntaxes for word arrays etc are also supported.
|
250
|
+
|
251
|
+
```ruby
|
252
|
+
[1, 2, 3, 4] # => [1, 2, 3, 4]
|
253
|
+
%w[foo bar baz] # => ["foo", "bar", "baz"]
|
254
|
+
```
|
16
255
|
|
17
|
-
|
256
|
+
#### Hash
|
18
257
|
|
19
|
-
|
258
|
+
Inside a generated ruby script, a function `__hash` is available which
|
259
|
+
creates a new hash. This is also available in javascript as `Opal.hash`
|
260
|
+
and simply returns a new instance of the `Hash` class.
|
20
261
|
|
21
|
-
|
262
|
+
```ruby
|
263
|
+
{ :foo => 100, :baz => 700 } # => __hash("foo", 100, "baz", 700)
|
264
|
+
{ foo: 42, bar: [1, 2, 3] } # => __hash("foo", 42, "bar", [1, 2, 3])
|
265
|
+
```
|
266
|
+
|
267
|
+
#### Range
|
268
|
+
|
269
|
+
Similar to hash, there is a function `__range` available to create
|
270
|
+
range instances.
|
271
|
+
|
272
|
+
```ruby
|
273
|
+
1..4 # => __range(1, 4, true)
|
274
|
+
3...7 # => __range(3, 7, false)
|
275
|
+
```
|
276
|
+
|
277
|
+
### Methods
|
278
|
+
|
279
|
+
A ruby method is just compiled directly into a function definition.
|
280
|
+
These functions are added to the constructor's prototype so they can
|
281
|
+
be called just like any javascript function. All ruby methods are
|
282
|
+
defined with a `$` prefix to try and isolate them from javascript
|
283
|
+
methods.
|
284
|
+
|
285
|
+
#### Method Calls
|
286
|
+
|
287
|
+
All method arguments are passed to the native function just like normal
|
288
|
+
javascript function calls. Therefore, the given ruby code:
|
289
|
+
|
290
|
+
```ruby
|
291
|
+
do_something 1, 2, 3
|
292
|
+
self.length
|
293
|
+
[1, 2, 3].push 5
|
294
|
+
```
|
295
|
+
|
296
|
+
Will be compiled into the easy to read javascript:
|
297
|
+
|
298
|
+
```javascript
|
299
|
+
this.$do_something(1, 2, 3);
|
300
|
+
this.$length();
|
301
|
+
[1, 2, 3].$push(5);
|
302
|
+
```
|
303
|
+
|
304
|
+
There are some certain characters which are valid as ruby method names
|
305
|
+
but not as javascript identifiers. These method calls are encoded to
|
306
|
+
keep the generated method names sane.
|
307
|
+
|
308
|
+
```ruby
|
309
|
+
self.loaded? # => this.$loaded$p()
|
310
|
+
self.load! # => this.$load$b()
|
311
|
+
self.loaded = true # => this.$loaded$e(true)
|
312
|
+
self << :bar # => this.$lshift$("bar")
|
313
|
+
```
|
314
|
+
|
315
|
+
Finally, method calls with splat arguments are also supported:
|
316
|
+
|
317
|
+
```ruby
|
318
|
+
self.push *[1, 2, 3]
|
319
|
+
# => this.$push.apply(this, [1, 2, 3])
|
320
|
+
```
|
321
|
+
|
322
|
+
#### Optimized Math Operators
|
323
|
+
|
324
|
+
In ruby, all math operators are method calls, but compiling this into
|
325
|
+
javascript would end up being too slow. For this reason, math
|
326
|
+
operators are optimized to test first if the receiver is a number, and
|
327
|
+
if so then to just carry out the math call.
|
328
|
+
|
329
|
+
```ruby
|
330
|
+
3 + 4
|
331
|
+
```
|
332
|
+
|
333
|
+
This ruby code will then be compiled into the following javascript:
|
334
|
+
|
335
|
+
```javascript
|
336
|
+
(a = 3, b = 4, typeof(a) === "number" ? a + b : a.$plus(b))
|
337
|
+
```
|
338
|
+
|
339
|
+
This ternary statement falls back on sending a method to the receiver
|
340
|
+
so all non-numeric receivers will still have the normal method call
|
341
|
+
being sent. This optimization makes math operators a **lot faster**.
|
342
|
+
Currently, the optimized method calls are `+`, `-`, `*` and `/`.
|
343
|
+
|
344
|
+
#### Method Definitions
|
345
|
+
|
346
|
+
Methods are implemented as regular javascript functions. Assuming the
|
347
|
+
following method is defined inside a class body:
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
def to_s
|
351
|
+
inspect
|
352
|
+
end
|
353
|
+
```
|
354
|
+
|
355
|
+
This would generate the following javascript. (`def.` is a local
|
356
|
+
variable set to be the class's instance prototype. It is used
|
357
|
+
for minimization of code as well as trying to be readable).
|
358
|
+
|
359
|
+
```javascript
|
360
|
+
def.$to_s = function() {
|
361
|
+
return this.$inspect();
|
362
|
+
};
|
363
|
+
```
|
364
|
+
|
365
|
+
The defined name retains the `$` prefix outlined above, and the `self`
|
366
|
+
value for the method is `this`, which will be the receiver.
|
367
|
+
|
368
|
+
Normal arguments, splat args and optional args are all supported:
|
369
|
+
|
370
|
+
```ruby
|
371
|
+
def norm(a, b, c)
|
372
|
+
|
373
|
+
end
|
374
|
+
|
375
|
+
def opt(a, b = 100)
|
376
|
+
|
377
|
+
end
|
378
|
+
|
379
|
+
def rest(a, *b)
|
380
|
+
|
381
|
+
end
|
382
|
+
```
|
383
|
+
|
384
|
+
The generated code reads as expected:
|
385
|
+
|
386
|
+
```javascript
|
387
|
+
def.$norm = function(a, b, c) {
|
388
|
+
return nil;
|
389
|
+
};
|
390
|
+
|
391
|
+
def.$opt = function(a, b) {
|
392
|
+
if (b == null) b = 100;
|
393
|
+
return nil;
|
394
|
+
};
|
395
|
+
|
396
|
+
def.$rest = function(a, b) {
|
397
|
+
b = __slice.call(arguments, 1);
|
398
|
+
return nil;
|
399
|
+
};
|
400
|
+
```
|
401
|
+
|
402
|
+
Currently, in opal there is no argument length checking to ensure that
|
403
|
+
the correct number of arguments get passed to a function. This can be
|
404
|
+
enabled in debug mode, but is not included in production builds as it
|
405
|
+
adds a lot of overhead to **every** method call.
|
406
|
+
|
407
|
+
### Compiled Files
|
408
|
+
|
409
|
+
As described above, a compiled ruby source gets generated into a string
|
410
|
+
of javascript code that is wrapped inside an anonymous function. This
|
411
|
+
looks similar to the following:
|
412
|
+
|
413
|
+
```javascript
|
414
|
+
(function(undefined) {
|
415
|
+
var nil = Opal.nil, __slice = Opal.slice, __klass = Opal.klass;
|
416
|
+
// generated code
|
417
|
+
}).call(Opal.top);
|
418
|
+
```
|
419
|
+
|
420
|
+
This function gets called with `Opal.top` as the context, which is the
|
421
|
+
top level object available to ruby (`main`). Inside the function, `nil`
|
422
|
+
is assigned to ensure a local copy is available, as well as all the
|
423
|
+
helper methods used within the generated file. There is no return value
|
424
|
+
from these functions as they are not used anywhere.
|
425
|
+
|
426
|
+
As a complete example, assuming the following code:
|
427
|
+
|
428
|
+
```ruby
|
429
|
+
puts "foo"
|
430
|
+
```
|
431
|
+
|
432
|
+
This would compile directly into:
|
433
|
+
|
434
|
+
```javascript
|
435
|
+
(function(undefined) {
|
436
|
+
var nil = Opal.nil;
|
437
|
+
this.$puts("foo");
|
438
|
+
}).call(Opal.top);
|
439
|
+
```
|
440
|
+
|
441
|
+
Most of the helpers are no longer present as they are not used in this
|
442
|
+
example.
|
443
|
+
|
444
|
+
### Using compiled sources
|
445
|
+
|
446
|
+
If you write the generated code as above into a file `app.js` and add
|
447
|
+
that to your HTML page, then it is obvious that `"foo"` would be
|
448
|
+
written to the browser's console.
|
22
449
|
|
23
450
|
## License
|
24
451
|
|
25
|
-
Opal is released under the MIT license.
|
452
|
+
Opal is released under the MIT license.
|
453
|
+
|
454
|
+
## Change Log
|
455
|
+
|
456
|
+
**0.3.19** _(30 May 2012)_
|
457
|
+
|
458
|
+
* Add BasicObject as the root class
|
459
|
+
* Add `Opal.define` and `Opal.require` for requiring files
|
460
|
+
* Builder uses a `main` option to dictate which file to require on load
|
461
|
+
* Completely revamp runtime to reduce helper methods
|
462
|
+
* Allow native bridges (Array, String, etc) to be subclassed
|
463
|
+
* Make sure `.js` files can be built with `Opal::Builder`
|
464
|
+
* Include the current file name when raising parse errors
|
465
|
+
|
466
|
+
**0.3.18** _(20 May 2012)_
|
467
|
+
|
468
|
+
* Fix various core lib bugs
|
469
|
+
* Completely remove `require` from corelib
|
470
|
+
* Improve Builder to detect dependencies in files
|
471
|
+
|
472
|
+
**0.3.17** _(19 May 2012)_
|
473
|
+
|
474
|
+
* Revamp of Builder and Parser tools
|
475
|
+
* Remove opal-repl
|
476
|
+
* Added a lot of specs for core lib
|
477
|
+
|
478
|
+
**0.3.16** _(15 January 2012)_
|
479
|
+
|
480
|
+
* Added HEREDOCS support in parser
|
481
|
+
* Parser now handles masgn (mass/multi assignments)
|
482
|
+
* More useful DependencyBuilder class to build gems dependencies
|
483
|
+
* Blocks no longer passed as an argument in method calls
|
484
|
+
|
485
|
+
**0.3.15**
|
486
|
+
|
487
|
+
* Initial Release.
|