opal 0.3.20 → 0.3.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/.gitignore +2 -7
  2. data/.rspec +2 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +8 -2
  5. data/LICENSE +2 -3
  6. data/README.md +106 -239
  7. data/Rakefile +58 -16
  8. data/bin/opal +1 -0
  9. data/core/array.rb +180 -160
  10. data/core/basic_object.rb +8 -4
  11. data/core/boolean.rb +6 -6
  12. data/core/class.rb +9 -15
  13. data/core/dir.rb +89 -0
  14. data/core/enumerable.rb +83 -86
  15. data/core/error.rb +9 -4
  16. data/core/file.rb +85 -0
  17. data/core/hash.rb +67 -67
  18. data/core/kernel.rb +38 -42
  19. data/core/module.rb +57 -54
  20. data/core/numeric.rb +41 -41
  21. data/core/proc.rb +1 -5
  22. data/core/range.rb +11 -11
  23. data/core/regexp.rb +27 -22
  24. data/core/runtime.js +152 -221
  25. data/core/string.rb +86 -73
  26. data/core/time.rb +22 -18
  27. data/docs/post.html +9 -0
  28. data/docs/pre.html +32 -0
  29. data/lib/opal.rb +43 -3
  30. data/lib/opal/builder.rb +9 -26
  31. data/lib/opal/grammar.rb +1 -1
  32. data/lib/opal/grammar.y +1 -1
  33. data/lib/opal/lexer.rb +21 -15
  34. data/lib/opal/parser.rb +100 -111
  35. data/lib/opal/rake_task.rb +66 -0
  36. data/lib/opal/scope.rb +13 -5
  37. data/lib/opal/version.rb +1 -1
  38. data/opal.gemspec +2 -0
  39. data/spec/browser_spec.rb +28 -0
  40. data/spec/builder/lib_name_for_spec.rb +1 -6
  41. data/spec/grammar/alias_spec.rb +1 -1
  42. data/spec/grammar/and_spec.rb +1 -1
  43. data/spec/grammar/array_spec.rb +1 -1
  44. data/spec/grammar/attrasgn_spec.rb +1 -1
  45. data/spec/grammar/begin_spec.rb +1 -1
  46. data/spec/grammar/block_spec.rb +1 -1
  47. data/spec/grammar/break_spec.rb +1 -1
  48. data/spec/grammar/call_spec.rb +1 -1
  49. data/spec/grammar/class_spec.rb +1 -1
  50. data/spec/grammar/const_spec.rb +1 -1
  51. data/spec/grammar/cvar_spec.rb +1 -1
  52. data/spec/grammar/def_spec.rb +1 -1
  53. data/spec/grammar/false_spec.rb +1 -1
  54. data/spec/grammar/file_spec.rb +1 -1
  55. data/spec/grammar/gvar_spec.rb +1 -1
  56. data/spec/grammar/hash_spec.rb +1 -1
  57. data/spec/grammar/iasgn_spec.rb +1 -1
  58. data/spec/grammar/if_spec.rb +1 -1
  59. data/spec/grammar/iter_spec.rb +1 -1
  60. data/spec/grammar/ivar_spec.rb +1 -1
  61. data/spec/grammar/lambda_spec.rb +1 -1
  62. data/spec/grammar/lasgn_spec.rb +1 -1
  63. data/spec/grammar/line_spec.rb +1 -1
  64. data/spec/grammar/lvar_spec.rb +1 -1
  65. data/spec/grammar/masgn_spec.rb +1 -1
  66. data/spec/grammar/module_spec.rb +1 -1
  67. data/spec/grammar/nil_spec.rb +1 -1
  68. data/spec/grammar/not_spec.rb +1 -1
  69. data/spec/grammar/op_asgn1_spec.rb +1 -1
  70. data/spec/grammar/op_asgn2_spec.rb +1 -1
  71. data/spec/grammar/or_spec.rb +1 -1
  72. data/spec/grammar/return_spec.rb +1 -1
  73. data/spec/grammar/sclass_spec.rb +1 -1
  74. data/spec/grammar/self_spec.rb +1 -1
  75. data/spec/grammar/str_spec.rb +1 -1
  76. data/spec/grammar/super_spec.rb +1 -1
  77. data/spec/grammar/true_spec.rb +1 -1
  78. data/spec/grammar/undef_spec.rb +1 -1
  79. data/spec/grammar/unless_spec.rb +1 -1
  80. data/spec/grammar/while_spec.rb +1 -1
  81. data/spec/grammar/xstr_spec.rb +1 -1
  82. data/spec/grammar/yield_spec.rb +1 -1
  83. data/spec/spec_helper.rb +6 -1
  84. data/test/core/array/minus_spec.rb +13 -0
  85. data/test/core/enumerable/drop_while_spec.rb +0 -5
  86. data/test/core/range/case_compare_spec.rb +0 -1
  87. data/test/index.html +1 -1
  88. data/test/index.min.html +12 -0
  89. data/test/language/alias_spec.rb +0 -4
  90. data/test/language/fixtures/next.rb +62 -0
  91. data/test/language/metaclass_spec.rb +4 -4
  92. data/test/language/next_spec.rb +0 -63
  93. data/test/language/send_spec.rb +0 -5
  94. data/test/language/singleton_class_spec.rb +4 -0
  95. data/test/opal/array/subclassing_spec.rb +1 -1
  96. data/test/opal/class/bridge_class_spec.rb +2 -2
  97. data/test/opal/runtime/class_hierarchy_spec.rb +1 -2
  98. data/test/opal/runtime/method_missing_spec.rb +17 -0
  99. data/test/spec_helper.rb +4 -1
  100. metadata +32 -28
  101. data/docs/CNAME +0 -1
  102. data/docs/Rakefile +0 -55
  103. data/docs/css/styles.css +0 -50
  104. data/docs/css/syntax.css +0 -63
  105. data/docs/layout/post.html +0 -3
  106. data/docs/layout/pre.html +0 -11
  107. data/examples/dependencies/.gitignore +0 -1
  108. data/examples/dependencies/Gemfile +0 -6
  109. data/examples/dependencies/README.md +0 -41
  110. data/examples/dependencies/Rakefile +0 -10
  111. data/examples/dependencies/app.rb +0 -19
  112. data/examples/dependencies/build/.gitkeep +0 -0
  113. data/examples/dependencies/index.html +0 -13
  114. data/examples/hello_world/.gitignore +0 -1
  115. data/examples/hello_world/Gemfile +0 -3
  116. data/examples/hello_world/README.md +0 -27
  117. data/examples/hello_world/Rakefile +0 -23
  118. data/examples/hello_world/app.rb +0 -7
  119. data/examples/hello_world/index.html +0 -12
  120. data/lib/opal/builder_task.rb +0 -91
  121. data/spec/builder/build_order_spec.rb +0 -20
  122. data/test/opal/runtime/_methods_spec.rb +0 -48
data/.gitignore CHANGED
@@ -1,16 +1,11 @@
1
1
  .DS_Store
2
- doc
3
- /tmp
4
2
  *.gem
5
3
  .yardoc
6
- /vendor
7
4
  .bundle
8
5
  Gemfile.lock
9
6
  pkg/*
10
- /runtime/*.js
11
- /*.js
12
- /docs/gh-pages
7
+ /gh-pages
13
8
  /examples/**/*.js
14
9
  /build
15
10
  Gemfile.local
16
- Gemfile.local.lock
11
+ Gemfile.local.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format doc
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.3
6
+
7
+ before_script:
8
+ - "export DISPLAY=:99.0"
9
+ - "sh -e /etc/init.d/xvfb start"
10
+ - "bundle exec rake build dependencies spec"
11
+
data/Gemfile CHANGED
@@ -6,6 +6,12 @@ gem "rake"
6
6
  gem "racc"
7
7
 
8
8
  group :browser do
9
- gem "opal-spec"
10
- gem 'opal-dom'
9
+ gem 'opal-spec', :git => 'git://github.com/adambeynon/opal-spec.git'
10
+ gem 'opal-dom', :git => 'git://github.com/adambeynon/opal-dom.git'
11
+ gem 'capybara'
12
+ end
13
+
14
+ group :docs do
15
+ gem "redcarpet"
16
+ gem "albino"
11
17
  end
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (C) 2011 by Adam Beynon
1
+ Copyright (C) 2012 by Adam Beynon
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
@@ -16,5 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
16
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
17
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
18
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- THE SOFTWARE.
20
-
19
+ THE SOFTWARE.
data/README.md CHANGED
@@ -19,15 +19,11 @@ is a Freenode IRC channel at `#opal`.
19
19
  The Opal runtime and corelib are distributed here, and are required to
20
20
  run any code generated by opal.
21
21
 
22
- [Opal version 0.3.20](http://opalrb.org/opal.js) _(14.3kb Minified And Gzipped)_
22
+ [Opal version 0.3.21](http://opalrb.org/opal.js) _(13.1kb Minified And Gzipped)_
23
23
 
24
24
  ## Installation
25
25
 
26
- Opal comes distributed as a gem, so either install with:
27
-
28
- gem install opal
29
-
30
- Or add to your Gemfile:
26
+ Opal should be added to your Gemfile:
31
27
 
32
28
  ```ruby
33
29
  gem "opal"
@@ -35,139 +31,150 @@ gem "opal"
35
31
 
36
32
  ## Usage
37
33
 
38
- To quickly compile ruby code into javascript, use the `Opal.parse()`
39
- method which returns a string of javascript code:
34
+ The easiest way to use opal is to create a rake task using the
35
+ `RakeTask` helper class. Assuming you have a single ruby file in
36
+ your Opal app called `app.rb`:
40
37
 
41
38
  ```ruby
42
- require 'opal'
43
-
44
- Opal.parse("puts 'hello world'")
39
+ # app.rb
40
+ puts "Hello world"
45
41
  ```
46
42
 
47
- This will return a string of javascript:
43
+ Then create a rake task similar to:
48
44
 
49
- ```javascript
50
- (function() {
51
- var self = Opal.top;
52
- self.$puts('hello world');
53
- })();
45
+ ```ruby
46
+ # Rakefile
47
+ require 'opal/rake_task'
48
+
49
+ Opal::RakeTask.new do |t|
50
+ t.files = ['app.rb']
51
+ end
54
52
  ```
55
53
 
56
- This can then be written to a file and run in any browser. `Opal` is a
57
- global javascript variable that holds all the ruby classes and methods.
58
- `Opal.top` points to the top object in opal, so any file will have this
59
- object as its top level `self` variable.
54
+ ### Building the app
60
55
 
61
- ### Creating a rake task
56
+ Building the app is as simple as running:
62
57
 
63
- Using a Rakefile makes it simple to build your application code.
64
- Assuming your code is in a file `app.rb`, add a rake task:
58
+ ```
59
+ rake opal:build
60
+ ```
61
+
62
+ This will build all your listed files into `build/app_name.js`. The
63
+ output name is based on the directory name. This can be overriden by
64
+ setting the `.name` property in the task:
65
65
 
66
66
  ```ruby
67
- # Rakefile
67
+ Opal::RakeTask.new do |t|
68
+ t.files = ['app.rb']
69
+ t.name = 'my_awesome_app'
70
+ end
71
+ ```
68
72
 
69
- require 'opal'
73
+ ### Building opal runtime
70
74
 
71
- desc "Build opal application"
72
- task :build do
73
- src = File.read 'app.rb'
74
- js = Opal.parse src
75
+ To run the app in the browser, the opal runtime is required. This can
76
+ be built using:
75
77
 
76
- File.open('app.js', 'w+') do |out|
77
- out.write js
78
- end
78
+ ```
79
+ rake opal:dependencies
80
+ ```
81
+
82
+ Which will build `opal.js` into `./build`.
83
+
84
+ The output directory can also be overriden inside the rake task:
85
+
86
+ ```ruby
87
+ Opal::RakeTask.new do |t|
88
+ t.files = ['app.rb']
89
+ t.build_dir = 'out_dir'
79
90
  end
80
91
  ```
81
92
 
82
- Running `rake build` will then read your app code, compile it and then
83
- write it out to a file ready to load in a web browser.
93
+ The output directory will be created if it doesn't exist.
84
94
 
85
- ### Setting up html file
95
+ ### Running the app
86
96
 
87
- The generated `app.js` file can just be added into any HTML page. The
88
- opal runtime needs to be loaded first (you can download that above).
97
+ The two compiled files need to be added to a html page so that they
98
+ can run in the browser:
89
99
 
90
100
  ```html
91
101
  <!doctype html>
92
102
  <html>
93
103
  <head>
94
- <title>Test Opal App</title>
104
+ <title>My awesome Opal app</title>
105
+
106
+ <script src="build/opal.js"></script>
107
+ <script src="build/my_awesome_app.js"></script>
108
+
109
+ <script>
110
+ // Run opal app
111
+ Opal.require('app')
112
+ </script>
95
113
  </head>
96
114
  <body>
97
- <script src="opal.js"></script>
98
- <script src="app.js"></script>
99
115
  </body>
100
116
  </html>
101
117
  ```
102
118
 
103
- When using `Opal.parse()` as above, the generated code will be run
104
- as soon as the page loads. Open the browsers console and you should
105
- see the message printed to the console.
119
+ If you open the html file, observe the console and you should see
120
+ `"Hello World"` printed to the console.
106
121
 
107
- ## Builder and Dependency Builder
122
+ It is necessary to run `Opal.require('app')` as all files built for
123
+ opal are registered so that they can be required inside the ruby
124
+ code.
108
125
 
109
- The previous example was useful for building very simple apps using
110
- opal. For more complex apps with dependencies, Opal provides useful
111
- rake tasks to get started. Assuming opal is in your lib path, create
112
- a `Rakefile` similar to:
126
+ ### Adding dependencies
113
127
 
114
- ```ruby
115
- # Rakefile
128
+ The `opal:dependencies` rake task above can be used to build gems which
129
+ are designed to run in the browser. `opal-dom` is a gem that given opal
130
+ access to the DOM in the browser.
116
131
 
117
- require 'opal'
132
+ `opal-dom` first needs to be installed as a gem (currently it is only
133
+ available from git):
118
134
 
119
- Opal::BuilderTask.new do |t|
120
- t.name = 'my-first-app'
121
- t.dependencies = ['opal-spec']
122
- t.files = ['app.rb']
123
- end
135
+ ```ruby
136
+ # Gemfile
137
+ gem "opal"
138
+ gem "opal-dom", :git => 'git://github.com/adambeynon/opal-dom.git'
124
139
  ```
125
140
 
126
- This simple rake task is all you need to build an app with its
127
- dependencies.
128
-
129
- ### Building dependencies
130
-
131
- To build the opal runtime `opal.js`, as well as `opal-spec` into
132
- `build/`, run the simple rake task:
141
+ Then add it to the dependencies to build:
133
142
 
134
143
  ```ruby
135
- rake dependencies
144
+ Opal::RakeTask.new do |t|
145
+ t.files = ['app.rb']
146
+ t.dependencies = ['opal-dom']
147
+ end
136
148
  ```
137
149
 
138
- This will try and find the `opal-spec` gem installed, so either
139
- install globally with `gem install opal-spec`, or add it to your
140
- Gemfile as `gem "opal-spec"` and run `bundle install`.
150
+ Running `rake opal:dependencies` now will also build
151
+ `build/opal-dom.js`.
141
152
 
142
- ### Building app
143
-
144
- To build the listed files into your application, run:
153
+ We can now update our application code:
145
154
 
146
155
  ```ruby
147
- rake build
156
+ # app.rb
157
+ require 'opal-dom'
158
+
159
+ alert "Hello!"
148
160
  ```
149
161
 
150
- This will build to `/build/my-first-app.js`. To customize the output
151
- filename, change the `name` property in the raketask.
162
+ And rebuild:
152
163
 
153
- ### Running the app
164
+ ```
165
+ rake opal:build
166
+ ```
154
167
 
155
- You should now be able to run the built app using a standard HTML page.
168
+ And add `opal-dom` to the html page:
156
169
 
157
170
  ```html
158
- <!doctype html>
159
- <html>
160
- <head>
161
- <title>Test Opal App</title>
162
- </head>
163
- <body>
164
- <script src="build/opal.js"></script>
165
- <script src="build/opal-spec.js"></script>
166
- <script src="build/my-first-app.js"></script>
167
- </body>
168
- </html>
171
+ <script src="build/opal.js"></script>
172
+ <script src="build/opal-dom.js"></script>
173
+ <script src="build/app.js"></script>
169
174
  ```
170
175
 
176
+ Now running the app should cause an alert box to display.
177
+
171
178
  ## Features And Implementation
172
179
 
173
180
  Opal is a source-to-source compiler, so there is no VM as such and the
@@ -176,7 +183,7 @@ directly to underlying javascript features and objects where possible.
176
183
 
177
184
  ### Literals
178
185
 
179
- **self** is always compiled to `this`. Any context inside the generated
186
+ **self** is always compiled to `self`. Any context inside the generated
180
187
  code is usually a function body; whether it be a method body, a block,
181
188
  a class/module body or the file itself.
182
189
 
@@ -195,7 +202,7 @@ files points to a special object which is just an instance of the ruby
195
202
  nil # => nil
196
203
  true # => true
197
204
  false # => false
198
- self # => this
205
+ self # => self
199
206
  ```
200
207
 
201
208
  #### Strings
@@ -262,94 +269,6 @@ range instances.
262
269
  3...7 # => __range(3, 7, false)
263
270
  ```
264
271
 
265
- ### Classes and Modules
266
-
267
- A compiled class or module body is simply just an anonymous javascript
268
- function that is used to keep any internal variables from escaping.
269
-
270
- Classes and modules themselves are created as named functions (which is
271
- used purely to aid debugging). Classes and modules may also be
272
- re-opended, so they are put through a runtime function `__klass` for
273
- classes, and `__module` for modules.
274
-
275
- For example, the `Kernel` module is generated as:
276
-
277
- ```javascript
278
- (function(__base) {
279
- function Kernel(){};
280
- Kernel = __module(__base, "Kernel", Kernel);
281
- var Kernel_prototype = Kernel.prototype;
282
-
283
- Kernel_prototype.$class = function() {
284
- return this._real;
285
- };
286
-
287
- Kernel._donate('$class', ...);
288
- })(__scope);
289
- ```
290
-
291
- Firstly, the `__scope` is passed into the function which is used
292
- for defining constants, which means that if `Kernel` gets defined
293
- here then it can be set as a constant for the given scope. The
294
- named function (Kernel) follows, as well as the `__module()` call
295
- which simply sets the up the module if it doesn't already exist. If
296
- it does exist, then it overwrites the previous `Kernel` local variable.
297
-
298
- Next, `Kernel_prototype` is made a local variable to improve
299
- minimization of the code, then all the Kernel methods are set on the
300
- prototype. Modules cannot be instantiated in Opal (just like ruby), but
301
- the use of prototypes is to improve performance.
302
-
303
- Finally, the call to `Kernel._donate()` is to pass any defined methods
304
- into classes that have included `Kernel`, which is just `Object`. This
305
- method then adds all of Kernels methods into Objects prototype as this
306
- is the most efficient way to try and emulate rubys method chain.
307
-
308
- ### Methods
309
-
310
- A ruby method is just compiled directly into a function definition.
311
- These functions are added to the constructor's prototype so they can
312
- be called just like any javascript function. All ruby methods are
313
- defined with a `$` prefix to try and isolate them from javascript
314
- methods.
315
-
316
- #### Method Calls
317
-
318
- All method arguments are passed to the native function just like normal
319
- javascript function calls. Therefore, the given ruby code:
320
-
321
- ```ruby
322
- do_something 1, 2, 3
323
- self.length
324
- [1, 2, 3].push 5
325
- ```
326
-
327
- Will be compiled into the easy to read javascript:
328
-
329
- ```javascript
330
- this.$do_something(1, 2, 3);
331
- this.$length();
332
- [1, 2, 3].$push(5);
333
- ```
334
-
335
- There are some certain characters which are valid as ruby method names
336
- but not as javascript identifiers. These method calls are encoded to
337
- keep the generated method names sane.
338
-
339
- ```ruby
340
- self.loaded? # => this.$loaded$p()
341
- self.load! # => this.$load$b()
342
- self.loaded = true # => this.$loaded$e(true)
343
- self << :bar # => this.$lshift$("bar")
344
- ```
345
-
346
- Finally, method calls with splat arguments are also supported:
347
-
348
- ```ruby
349
- self.push *[1, 2, 3]
350
- # => this.$push.apply(this, [1, 2, 3])
351
- ```
352
-
353
272
  #### Optimized Math Operators
354
273
 
355
274
  In ruby, all math operators are method calls, but compiling this into
@@ -364,7 +283,7 @@ if so then to just carry out the math call.
364
283
  This ruby code will then be compiled into the following javascript:
365
284
 
366
285
  ```javascript
367
- (a = 3, b = 4, typeof(a) === "number" ? a + b : a.$plus(b))
286
+ (a = 3, b = 4, typeof(a) === "number" ? a + b : /* method call */)
368
287
  ```
369
288
 
370
289
  This ternary statement falls back on sending a method to the receiver
@@ -372,68 +291,10 @@ so all non-numeric receivers will still have the normal method call
372
291
  being sent. This optimization makes math operators a **lot faster**.
373
292
  Currently, the optimized method calls are `+`, `-`, `*` and `/`.
374
293
 
375
- #### Method Definitions
294
+ ### method_missing
376
295
 
377
- Methods are implemented as regular javascript functions. Assuming the
378
- following method is defined inside a class body:
379
-
380
- ```ruby
381
- def to_s
382
- inspect
383
- end
384
- ```
385
-
386
- This would generate the following javascript. (`Array_prototype` is
387
- an example and would assume this method definition is inside the
388
- `Array` class body).
389
-
390
- ```javascript
391
- Array_prototype.$to_s = function() {
392
- return this.$inspect();
393
- };
394
- ```
395
-
396
- The defined name retains the `$` prefix outlined above, and the `self`
397
- value for the method is `this`, which will be the receiver.
398
-
399
- Normal arguments, splat args and optional args are all supported:
400
-
401
- ```ruby
402
- def norm(a, b, c)
403
-
404
- end
405
-
406
- def opt(a, b = 100)
407
-
408
- end
409
-
410
- def rest(a, *b)
411
-
412
- end
413
- ```
414
-
415
- The generated code reads as expected:
416
-
417
- ```javascript
418
- Array_prototype.$norm = function(a, b, c) {
419
- return nil;
420
- };
421
-
422
- Array_prototype.$opt = function(a, b) {
423
- if (b == null) b = 100;
424
- return nil;
425
- };
426
-
427
- Array_prototype.$rest = function(a, b) {
428
- b = __slice.call(arguments, 1);
429
- return nil;
430
- };
431
- ```
432
-
433
- Currently, in opal there is no argument length checking to ensure that
434
- the correct number of arguments get passed to a function. This can be
435
- enabled in debug mode, but is not included in production builds as it
436
- adds a lot of overhead to **every** method call.
296
+ Method missing is fully supported in Opal. It is implemented as
297
+ efficiently as possible.
437
298
 
438
299
  ### Logic and conditionals
439
300
 
@@ -696,6 +557,12 @@ Opal is released under the MIT license.
696
557
 
697
558
  ## Change Log
698
559
 
560
+ **0.3.21** _(16 July 2012)_
561
+
562
+ * Add `method_missing` support to all objects and classes
563
+ * Add `Opal.build_gem()` method to quickly build installed gem
564
+ * Add `Opal.build_files()` method to build directories of files
565
+
699
566
  **0.3.20** _(23 June 2012)_
700
567
 
701
568
  * Merge JSON into core. JSON module and various #to_json methods are