opal 0.8.0 → 0.8.1.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/CHANGELOG.md +10 -0
- data/docs/compiled_ruby.md +425 -0
- data/docs/compiler.md +50 -0
- data/docs/compiler_directives.md +40 -34
- data/docs/configuring_gems.md +148 -0
- data/docs/encoding.md +13 -0
- data/docs/faq.md +17 -0
- data/docs/getting_started.md +30 -0
- data/docs/jquery.md +264 -0
- data/docs/libraries.md +36 -0
- data/docs/promises.md +64 -0
- data/docs/rails.md +116 -0
- data/docs/rspec.md +98 -0
- data/docs/sinatra.md +79 -0
- data/docs/source_maps.md +49 -0
- data/docs/static_applications.md +63 -0
- data/docs/templates.md +150 -0
- data/docs/unsupported_features.md +27 -0
- data/docs/using_sprockets.md +72 -0
- data/lib/opal/sprockets/processor.rb +15 -9
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/variables.rb +1 -1
- data/spec/lib/spec_helper.rb +2 -0
- data/spec/lib/sprockets/processor_spec.rb +11 -0
- metadata +22 -6
- data/lib/opal/sprockets/cache_key_fix.rb +0 -17
data/docs/compiler.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Opal Compiler
|
2
|
+
|
3
|
+
Opal is a source to source compiler. It accepts ruby code as a string and
|
4
|
+
generates javascript code which can be run in any environment. Generated
|
5
|
+
code relies on the opal runtime which provides the class system and some
|
6
|
+
other runtime helpers.
|
7
|
+
|
8
|
+
## Compiler stages
|
9
|
+
|
10
|
+
The compiler can be broken down into 3 separate stages:
|
11
|
+
|
12
|
+
* lexing
|
13
|
+
* parsing
|
14
|
+
* code generation
|
15
|
+
|
16
|
+
### Lexer
|
17
|
+
|
18
|
+
The [opal lexer][lexer] is implemented in pure ruby using
|
19
|
+
the `StringScanner` class from the opal stdlib. The source code is scanned
|
20
|
+
and tokens are then provided to the parser. This process simply converts
|
21
|
+
the ruby code given as a string, into a list of tokens representing the
|
22
|
+
parts of the ruby code.
|
23
|
+
|
24
|
+
### Parser
|
25
|
+
|
26
|
+
The [opal parser][parser] is implemented using a standard
|
27
|
+
bison like syntax, but relies on `racc`, a ruby implementation of yacc/bison
|
28
|
+
which is again available in the standard library. The parser takes these tokens
|
29
|
+
generated by the lexer and builds a syntax tree representing the ruby code.
|
30
|
+
This syntax tree is represented by [sexps][sexps]. As
|
31
|
+
ruby is such a complex and dynamic language, there is a lot of interaction
|
32
|
+
between the parser and the lexer, namely through a preserved `lex_state`.
|
33
|
+
|
34
|
+
### Code generation
|
35
|
+
|
36
|
+
The [opal compiler][compiler] takes these sexps from the parser
|
37
|
+
and generates ruby code from them. Each type of sexp has [its own node type][base-node]
|
38
|
+
used to generate javascript. Each node creates an array of one or more
|
39
|
+
[fragments][fragments] which are the concatendated together to
|
40
|
+
form the final javascript. Fragments are used as they contain the generated
|
41
|
+
code as well as a reference back to the original sexp which is useful for
|
42
|
+
generating source maps afterwards.
|
43
|
+
|
44
|
+
|
45
|
+
[lexer]: https://github.com/opal/opal/tree/master/lib/opal/parser/lexer.rb
|
46
|
+
[parser]: https://github.com/opal/opal/tree/master/lib/opal/parser/grammar.y
|
47
|
+
[sexps]: https://github.com/opal/opal/tree/master/lib/opal/parser/sexp.rb
|
48
|
+
[compiler]: https://github.com/opal/opal/tree/master/lib/opal/compiler.rb
|
49
|
+
[fragments]: https://github.com/opal/opal/tree/master/lib/opal/fragment.rb
|
50
|
+
[base-node]: https://github.com/opal/opal/tree/master/lib/opal/nodes/base.rb
|
data/docs/compiler_directives.md
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
# @title Compiler Directives
|
2
|
-
|
3
1
|
# Compiler Directives
|
4
2
|
|
5
3
|
The Opal compiler supports some special directives that can optimize or
|
6
|
-
enhance the output of compiled
|
4
|
+
enhance the output of compiled Ruby code to suit the Ruby environment.
|
7
5
|
|
8
6
|
## Require Directive
|
9
7
|
|
@@ -20,22 +18,22 @@ Assuming we have a file `foo.rb`:
|
|
20
18
|
require 'baz'
|
21
19
|
|
22
20
|
The compiler will collect these two dependencies, and then `Builder`
|
23
|
-
will attempt to discover them within the
|
21
|
+
will attempt to discover them within the Opal load path to also compile
|
24
22
|
them into the target output. If these dependencies cannot be resolved,
|
25
23
|
then a compile time error will be thrown.
|
26
24
|
|
27
25
|
#### Dynamic Requires
|
28
26
|
|
29
27
|
Opal only supports hard-coded requires. This means that any dynamically
|
30
|
-
generated require
|
28
|
+
generated require statements cannot be discovered. Opal may raise an
|
31
29
|
error or just produce a warning if a dynamic require is used. A dynamic
|
32
30
|
require is any require that cannot be resolved using static analysis. A
|
33
|
-
common use case of dynamic requires is to include a directory of
|
31
|
+
common use case of dynamic requires is to include a directory of Ruby
|
34
32
|
files. In this case, see `require_tree` below.
|
35
33
|
|
36
34
|
### `require_relative`
|
37
35
|
|
38
|
-
`require_relative` is also supported by
|
36
|
+
`require_relative` is also supported by Opal's compiler for ahead-of-time
|
39
37
|
inclusion.
|
40
38
|
|
41
39
|
# foo.rb
|
@@ -46,8 +44,8 @@ This example will try to resolve `bar.rb` in the same directory.
|
|
46
44
|
### `autoload`
|
47
45
|
|
48
46
|
`autoload` is used to load a modules and classes within a modules
|
49
|
-
namespace.
|
50
|
-
resolved in
|
47
|
+
namespace. As long as the string argument given to `autoload` can be
|
48
|
+
resolved in Opal's load paths, in the same way as `require`, then these
|
51
49
|
referenced dependencies will also be compiled.
|
52
50
|
|
53
51
|
# foo.rb
|
@@ -59,7 +57,7 @@ In this example, `bar.rb` will also be required.
|
|
59
57
|
|
60
58
|
### `require_tree`
|
61
59
|
|
62
|
-
`require_tree` can be used as an Opal
|
60
|
+
`require_tree` can be used as an Opal-friendly alternative to globbing
|
63
61
|
over a directory to require a list of dependencies.
|
64
62
|
|
65
63
|
# foo.rb
|
@@ -70,58 +68,66 @@ directory and also compile them to the output. At runtime this method
|
|
70
68
|
will then loop over all modules defined, and require them if they match
|
71
69
|
that given module path.
|
72
70
|
|
73
|
-
Note: The given directory **must** be inside
|
71
|
+
Note: The given directory **must** be inside Opal's load path, otherwise
|
74
72
|
no files will be compiled.
|
75
73
|
|
76
|
-
### Handling non-
|
74
|
+
### Handling non-Ruby requirements
|
77
75
|
|
78
|
-
Opal's `require` method is also special as it allows non-
|
76
|
+
Opal's `require` method is also special as it allows non-Ruby source
|
79
77
|
files to be required and generated in the output. The obvious example of
|
80
|
-
this is requiring
|
81
|
-
treated as first class citizens in Opal. The
|
78
|
+
this is requiring JavaScript source files. JavaScript sources are
|
79
|
+
treated as first class citizens in Opal. The Opal gem also supports
|
82
80
|
compiling `.erb` files using the same process.
|
83
81
|
|
84
82
|
## Opal Specific Code Compilation
|
85
83
|
|
86
84
|
A special case `if` and `unless` statements can hide or show blocks of
|
87
85
|
code from the Opal compiler. These check against `RUBY_ENGINE` or
|
88
|
-
`RUBY_PLATFORM`. As these are valid
|
89
|
-
that exist in all
|
86
|
+
`RUBY_PLATFORM`. As these are valid Ruby statements against constants
|
87
|
+
that exist in all Ruby runtimes, they will not affect any running code:
|
90
88
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
89
|
+
```ruby
|
90
|
+
if RUBY_ENGINE == 'opal'
|
91
|
+
# this code compiles
|
92
|
+
else
|
93
|
+
# this code never compiles
|
94
|
+
end
|
95
|
+
```
|
96
96
|
|
97
97
|
Unless statements are also supported:
|
98
98
|
|
99
|
-
|
100
|
-
|
101
|
-
|
99
|
+
```ruby
|
100
|
+
unless RUBY_ENGINE == 'opal'
|
101
|
+
# this code will not run
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
102
105
|
|
103
106
|
Also `!=` statements work:
|
104
107
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
+
```ruby
|
109
|
+
if RUBY_ENGINE != 'opal'
|
110
|
+
puts 'do not run this code'
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
108
114
|
|
109
|
-
These blocks of code
|
110
|
-
compile so will never be in the output
|
111
|
-
particularly useful for using code in both
|
115
|
+
These blocks of code don't run at all at runtime, but they also never
|
116
|
+
compile so will never be in the output JavaScript code. This is
|
117
|
+
particularly useful for using code in both MRI and Opal.
|
112
118
|
|
113
119
|
Some uses are:
|
114
120
|
|
115
121
|
* Avoid `require` statements being picked up by Opal compile time
|
116
122
|
require handling.
|
117
123
|
|
118
|
-
* To stop certain requires taking place for
|
124
|
+
* To stop certain requires taking place for Opal (and vice-versa for
|
119
125
|
shared libraries).
|
120
126
|
|
121
|
-
* To wrap x-strings which might break in compiled
|
127
|
+
* To wrap x-strings which might break in compiled JavaScript output.
|
122
128
|
|
123
129
|
* To simply avoid compiling large blocks of code that are not needed
|
124
|
-
in the
|
130
|
+
in the JavaScript/Opal version of an app.
|
125
131
|
|
126
132
|
In all these examples `RUBY_PLATFORM` can be used instead of
|
127
133
|
`RUBY_ENGINE`.
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# Configuring Gems For Opal
|
2
|
+
|
3
|
+
To configure a gem to run in Opal the gem will need a couple of things:
|
4
|
+
|
5
|
+
1. The opal gem running on a server (so the ruby code can get compiled to .js.)
|
6
|
+
2. The opal search path has to know to look for your gem when it is required.
|
7
|
+
|
8
|
+
This is done by having the following 2 lines in your outermost .rb file:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
require 'opal'
|
12
|
+
Opal.append_path File.expand_path('..', __FILE__).untaint
|
13
|
+
```
|
14
|
+
|
15
|
+
However it only makes sense to execute these lines outside of Opal, since what they do is set things up for Opal to find and compile the files to .js. So how these lines get added to your gem depends on whether the gem can usefully run in the normal server environment as well as in Opal, or just strictly in Opal.
|
16
|
+
|
17
|
+
For example, you have a gem that parses, validates, and gives details on email addresses. This gem would be just as useful on the server, as running
|
18
|
+
in the browser.
|
19
|
+
|
20
|
+
On the other hand you have a gem that does something with the DOM. There would be no point in making this gem available to the server.
|
21
|
+
|
22
|
+
Each case is detailed below, assuming you have a Gem file structure like this:
|
23
|
+
|
24
|
+
|
25
|
+
```
|
26
|
+
/lib
|
27
|
+
/your_gem_directory
|
28
|
+
/your_gem_file1.rb
|
29
|
+
/your_gem_file2.rb
|
30
|
+
/...
|
31
|
+
/version.rb
|
32
|
+
/your_gem.rb
|
33
|
+
```
|
34
|
+
|
35
|
+
## Configuring Gems To Run Everywhere
|
36
|
+
|
37
|
+
If your gem will work both in Opal and in a classic ruby environment
|
38
|
+
your outer .rb file (`your_gem.rb`) needs to look like this
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
# require all the files, regardless of whether this code is running
|
42
|
+
# to run on the server, or inside of Opal.
|
43
|
+
require_relative 'your_gem_directory/your_gem_file1.rb'
|
44
|
+
require_relative 'your_gem_directory/your_gem_file2.rb'
|
45
|
+
# etc
|
46
|
+
require_relative 'your_gem_directory/version'
|
47
|
+
unless RUBY_ENGINE == 'opal'
|
48
|
+
# Now if we are NOT running inside of opal, set things up so opal can find
|
49
|
+
# the files. The whole thing is rescued in case the opal gem is not available.
|
50
|
+
# This would happen if the gem is being used server side ONLY.
|
51
|
+
begin
|
52
|
+
require 'opal'
|
53
|
+
Opal.append_path File.expand_path('..', __FILE__).untaint
|
54
|
+
rescue LoadError
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
So lets see what happens here.
|
60
|
+
|
61
|
+
1. Somebody is going to require this file, perhaps implicitly (for example you are running in rails.)
|
62
|
+
2. Standard ruby is going to execute the `require`s, which will load your gem sources, then
|
63
|
+
3. because the `RUBY_ENGINE` is _not_ `opal`, Opal will be required, and your directory of sources added to Opal's search path.
|
64
|
+
4. Someplace else in some Opal code, the gem will _again_ be required, and so opal searches and finds the gem,
|
65
|
+
5. and runs this file again, _but now inside_ of the Opal environment. This time the `RUBY_ENGINE` _is_ Opal so the `require 'opal'` etc will not be executed.
|
66
|
+
|
67
|
+
The result is that you have two versions of the code, one in standard ruby, and a second compiled to .js and ready to be served.
|
68
|
+
|
69
|
+
## Configuring Gems To Run In Opal Only
|
70
|
+
|
71
|
+
If it makes no sense to run the code in standard Ruby (i.e. on the server) then the above code can look like this:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
# require all the files, only if Opal is executing
|
75
|
+
if RUBY_ENGINE == 'opal'
|
76
|
+
require_relative 'your_gem_directory/your_gem_file1.rb'
|
77
|
+
require_relative 'your_gem_directory/your_gem_file2.rb'
|
78
|
+
# etc
|
79
|
+
require_relative 'your_gem_directory/version'
|
80
|
+
else
|
81
|
+
# NOT running inside of opal, set things up
|
82
|
+
# so opal can find the files.
|
83
|
+
require 'opal'
|
84
|
+
Opal.append_path File.expand_path('..', __FILE__).untaint
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
## Testing
|
89
|
+
|
90
|
+
Regardless of which case your gem is designed for, you will want to make sure you test inside the Opal environment. If nothing else you will want to make sure you have all the above configuration setup correctly.
|
91
|
+
|
92
|
+
To do this add the following to your gemspec:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
spec.add_development_dependency "opal-rspec"
|
96
|
+
spec.add_development_dependency "opal"
|
97
|
+
```
|
98
|
+
|
99
|
+
Then setup the following in config.ru (assuming your specs are in the /spec directory.)
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
require 'bundler'
|
103
|
+
Bundler.require
|
104
|
+
|
105
|
+
require 'opal-rspec'
|
106
|
+
Opal.append_path File.expand_path('../spec', __FILE__)
|
107
|
+
|
108
|
+
run Opal::Server.new { |s|
|
109
|
+
s.main = 'opal/rspec/sprockets_runner'
|
110
|
+
s.append_path 'spec'
|
111
|
+
s.debug = false
|
112
|
+
s.index_path = 'spec/index.html.erb'
|
113
|
+
}
|
114
|
+
```
|
115
|
+
|
116
|
+
Finally create a index.html.erb file in the spec directory with the following contents:
|
117
|
+
|
118
|
+
```html
|
119
|
+
<!DOCTYPE html>
|
120
|
+
<html>
|
121
|
+
<head>
|
122
|
+
</head>
|
123
|
+
<body>
|
124
|
+
<%= javascript_include_tag @server.main %>
|
125
|
+
</body>
|
126
|
+
</html>
|
127
|
+
```
|
128
|
+
|
129
|
+
With all this setup, you can run specs normally to test standard ruby execution, and then do
|
130
|
+
|
131
|
+
bundle exec rackup
|
132
|
+
|
133
|
+
and point your browser to localhost, and you will see your spec results running in the Opal environment.
|
134
|
+
|
135
|
+
Don't forget to checkout the added features of the opal-rspec gem such as async specs.
|
136
|
+
|
137
|
+
## Conditional Execution
|
138
|
+
|
139
|
+
In some cases you might have to check whether you are in Opal or not, just wrap the code in:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
if RUBY_ENGINE == 'opal'
|
143
|
+
# …
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
This might happen in your specs or in the actual gem code.
|
148
|
+
|
data/docs/encoding.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Encoding
|
2
|
+
|
3
|
+
Encoding support is partial and mostly given by the encoding set by the HTML page.
|
4
|
+
We suggest to always set encoding to UTF-8 explicitly:
|
5
|
+
|
6
|
+
```html
|
7
|
+
<!doctype html>
|
8
|
+
<html>
|
9
|
+
<head>
|
10
|
+
<meta charset="UTF-8" />
|
11
|
+
<!-- ... -->
|
12
|
+
```
|
13
|
+
|
data/docs/faq.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# FAQ
|
2
|
+
|
3
|
+
### Why does Opal exist?
|
4
|
+
|
5
|
+
To try and keep ruby relevant in a world where client-side apps are making javascript the primary development platform.
|
6
|
+
|
7
|
+
### How compatible is Opal?
|
8
|
+
|
9
|
+
We run opal against [rubyspec](https://github.com/rubyspec/rubyspec) as our primary testing setup. We try to make Opal as compatible as possible, whilst also taking into account restrictions of Javascript when applicable. Opal supports the majority of ruby syntax features, as well as a very large part of the corelib implementation. We support method\_missing, modules, classes, instance\_exec, blocks, procs and lots lots more. Opal can compile and run Rspec unmodified, as well as self hosting the compiler at runtime.
|
10
|
+
|
11
|
+
### What version of ruby does Opal target?
|
12
|
+
|
13
|
+
We are running tests under ruby 2.0.0 conditions, but are mostly compatible with 1.9 level features.
|
14
|
+
|
15
|
+
### Why doesn't Opal support mutable strings?
|
16
|
+
|
17
|
+
All strings in Opal are immutable because ruby strings just get compiled direclty into javascript strings, which are immutable. Wrapping ruby strings as a custom Javascript object would add a lot of overhead as well as making interaction between ruby and javascript libraries more difficult.
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Getting Started
|
2
|
+
|
3
|
+
Opal is a ruby to javascript compiler, an implementation of the ruby corelib and stdlib, and associated gems for building fast client side web applications in ruby.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Opal is available as a gem, and can be installed via:
|
8
|
+
|
9
|
+
```
|
10
|
+
$ gem install opal
|
11
|
+
```
|
12
|
+
|
13
|
+
Or added to your Gemfile as:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'opal'
|
17
|
+
```
|
18
|
+
|
19
|
+
## Getting started with Opal
|
20
|
+
|
21
|
+
At its very core, opal provides a simple method of compiling a string of ruby into javascript that can run on top of the opal runtime, provided by `opal.js`:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
Opal.compile("[1, 2, 3].each { |a| puts a }")
|
25
|
+
# => "(function() { ... })()"
|
26
|
+
```
|
27
|
+
|
28
|
+
`opal` includes sprockets support to sprockets for compiling ruby (and erb) assets, and treating them as first class javascript citizens. It works in a similar way to coffeescript, where javascript files can simply require ruby sources, and ruby sources can require javascript and other ruby files.
|
29
|
+
|
30
|
+
This relies on the opal load path. Any gem containing opal code registers that directory to the opal load path. opal will then use all opal load paths when running sprockets instances. For rails applications, opal-rails does this automatically. For building a simple application, we have to do this manually.
|
data/docs/jquery.md
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
# JQuery
|
2
|
+
|
3
|
+
`opal-jquery` offers a nicer ruby-like syntax for JQuery (and Zepto). It is
|
4
|
+
useful for projects which cannot use `opal-browser` due to a reliance on jquery
|
5
|
+
for plugins or other libraries.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
foos = Element.find('.foo')
|
9
|
+
# => [<div class="foo">, ...]
|
10
|
+
|
11
|
+
foos.class
|
12
|
+
# => JQuery
|
13
|
+
|
14
|
+
foos.on(:click) do
|
15
|
+
alert "element was clicked"
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
### Getting Started
|
20
|
+
|
21
|
+
#### Installation
|
22
|
+
|
23
|
+
`opal-jquery` is distributed as a gem, and needs to be added to your `Gemfile`:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
# Gemfile
|
27
|
+
gem 'opal'
|
28
|
+
gem 'opal-jquery'
|
29
|
+
```
|
30
|
+
|
31
|
+
#### Usage
|
32
|
+
|
33
|
+
`opal-jquery` can now be easily added to your opal application sources using a
|
34
|
+
standard require:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# app/application.rb
|
38
|
+
require 'opal'
|
39
|
+
require 'jquery'
|
40
|
+
require 'opal-jquery'
|
41
|
+
|
42
|
+
alert "Hello from jquery + opal"
|
43
|
+
```
|
44
|
+
|
45
|
+
> **Note**: this file requires two important dependencies, `jquery` and `opal-jquery`.
|
46
|
+
> You need to bring your own `jquery.js` file as the gem does not include one. If
|
47
|
+
> you are using the asset pipeline with rails, then this should be available
|
48
|
+
> already, otherwise download a copy and place it into `app/` or whichever directory
|
49
|
+
> you are compiling assets from. You can alternatively require a zepto instance.
|
50
|
+
|
51
|
+
The `#alert` method is provided by `opal-jquery`. If the message displays, then
|
52
|
+
`jquery` support should be working.
|
53
|
+
|
54
|
+
### How does opal-jquery work
|
55
|
+
|
56
|
+
`opal-jquery` provides an `Element` class, whose instances are toll-free
|
57
|
+
bridged instances of jquery objects. Just like ruby arrays are just javascript
|
58
|
+
arrays, `Element` instances are just jquery objects. This makes interaction
|
59
|
+
with jquery plugins much easier.
|
60
|
+
|
61
|
+
Also, `Element` will try to bridge with Zepto if it cannot find jQuery loaded,
|
62
|
+
making it ideal for mobile applications as well.
|
63
|
+
|
64
|
+
### Interacting with the DOM
|
65
|
+
|
66
|
+
#### Finding Elements
|
67
|
+
|
68
|
+
opal-jquery provides the `Element` class, which can be used to find elements in
|
69
|
+
the current document:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
Element.find('#header')
|
73
|
+
```
|
74
|
+
|
75
|
+
`Element.find` is aliased to `Element[]`:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Element['.my-class']
|
79
|
+
```
|
80
|
+
|
81
|
+
These methods acts just like `$('selector')`, and can use any jQuery
|
82
|
+
compatible selector:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
Element.find('#navigation li:last')
|
86
|
+
```
|
87
|
+
|
88
|
+
The result is just a jQuery instance, which is toll-free bridged to
|
89
|
+
instances of the `Element` class in ruby:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
Element.find('.foo').class
|
93
|
+
# => Element
|
94
|
+
```
|
95
|
+
|
96
|
+
Instances of `Element` also have the `#find` method available for
|
97
|
+
finding elements within the scope of each DOM node represented by
|
98
|
+
the instance:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
el = Element.find('#header')
|
102
|
+
el.find '.foo'
|
103
|
+
# => #<Element .... >
|
104
|
+
```
|
105
|
+
|
106
|
+
#### Running code on document ready
|
107
|
+
|
108
|
+
Just like jQuery, opal-jquery requires the document to be ready to
|
109
|
+
be able to fully interact with the page. Any top level access should
|
110
|
+
use the `ready?` method:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
Document.ready? do
|
114
|
+
alert "document is ready to go!"
|
115
|
+
end
|
116
|
+
```
|
117
|
+
|
118
|
+
The `Kernel#alert` method is shown above too.
|
119
|
+
|
120
|
+
#### Event handling
|
121
|
+
|
122
|
+
The `Element#on` method is used to attach event handlers to elements:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
Element.find('#header').on :click do
|
126
|
+
puts "The header was clicked!"
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
Selectors can also be passed as a second argument to handle events
|
131
|
+
on certain children:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
Element.find('#header').on(:click, '.foo') do
|
135
|
+
puts "An element with a 'foo' class was clicked"
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
An `Event` instance is optionally passed to block handlers as well,
|
140
|
+
which is toll-free bridged to jquery events:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
Element.find('#my_link').on(:click) do |evt|
|
144
|
+
evt.stop_propagation
|
145
|
+
puts "stopped the event!"
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
You can access the element which triggered the event by `#current_target`.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
Document.on :click do |evt|
|
153
|
+
puts "clicked on: #{evt.current_target}"
|
154
|
+
end
|
155
|
+
```
|
156
|
+
|
157
|
+
#### CSS styles and classnames
|
158
|
+
|
159
|
+
The various jQuery methods are available on `Element` instances:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
foo = Element.find('.foo')
|
163
|
+
|
164
|
+
foo.add_class 'blue'
|
165
|
+
foo.remove_class 'foo'
|
166
|
+
foo.toggle_class 'selected'
|
167
|
+
```
|
168
|
+
|
169
|
+
There are also added convenience methods for opal-jquery:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
foo = Element.find('#header')
|
173
|
+
|
174
|
+
foo.class_name
|
175
|
+
# => 'red lorry'
|
176
|
+
|
177
|
+
foo.class_name = 'yellow house'
|
178
|
+
|
179
|
+
foo.class_name
|
180
|
+
# => 'yellow house'
|
181
|
+
```
|
182
|
+
|
183
|
+
`Element#css` also exists for getting/setting css styles:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
el = Element.find('#container')
|
187
|
+
el.css 'color', 'blue'
|
188
|
+
el.css 'color'
|
189
|
+
# => 'blue'
|
190
|
+
```
|
191
|
+
|
192
|
+
### HTTP/AJAX requests
|
193
|
+
|
194
|
+
jQuery's Ajax implementation is also wrapped in the top level HTTP
|
195
|
+
class.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
HTTP.get("/users/1.json") do |response|
|
199
|
+
puts response.body
|
200
|
+
# => "{\"name\": \"Adam Beynon\"}"
|
201
|
+
end
|
202
|
+
```
|
203
|
+
|
204
|
+
The block passed to this method is used as the handler when the request
|
205
|
+
succeeds, as well as when it fails. To determine whether the request
|
206
|
+
was successful, use the `ok?` method:
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
HTTP.get("/users/2.json") do |response|
|
210
|
+
if response.ok?
|
211
|
+
alert "successful!"
|
212
|
+
else
|
213
|
+
alert "request failed :("
|
214
|
+
end
|
215
|
+
end
|
216
|
+
```
|
217
|
+
|
218
|
+
It is also possible to use a different handler for each case:
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
request = HTTP.get("/users/3.json")
|
222
|
+
|
223
|
+
request.callback {
|
224
|
+
puts "Request worked!"
|
225
|
+
}
|
226
|
+
|
227
|
+
request.errback {
|
228
|
+
puts "Request didn't work :("
|
229
|
+
}
|
230
|
+
```
|
231
|
+
|
232
|
+
The request is actually triggered inside the `HTTP.get` method, but due
|
233
|
+
to the async nature of the request, the callback and errback handlers can
|
234
|
+
be added anytime before the request returns.
|
235
|
+
|
236
|
+
#### Handling responses
|
237
|
+
|
238
|
+
Web apps deal with JSON responses quite frequently, so there is a useful
|
239
|
+
`#json` helper method to get the JSON content from a request:
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
HTTP.get("/users.json") do |response|
|
243
|
+
puts response.body
|
244
|
+
puts response.json
|
245
|
+
end
|
246
|
+
|
247
|
+
# => "[{\"name\": \"Adam\"},{\"name\": \"Ben\"}]"
|
248
|
+
# => [{"name" => "Adam"}, {"name" => "Ben"}]
|
249
|
+
```
|
250
|
+
|
251
|
+
The `#body` method will always return the raw response string.
|
252
|
+
|
253
|
+
If an error is encountered, then the `#status_code` method will hold the
|
254
|
+
specific error code from the underlying request:
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
request = HTTP.get("/users/3.json")
|
258
|
+
|
259
|
+
request.callback { puts "it worked!" }
|
260
|
+
|
261
|
+
request.errback { |response|
|
262
|
+
puts "failed with status #{response.status_code}"
|
263
|
+
}
|
264
|
+
```
|