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.
@@ -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
@@ -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 ruby code to suit the ruby environment.
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 opal load path to also compile
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 statemnts cannot be discoeverd. Opal may raise an
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 ruby
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 opals compiler for ahead-of-time
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. Aslong as the string argument given to `autoload` can be
50
- resolved in Opals load paths, in the same way as `require`, then these
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 friendly alternative to globbing
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 Opals load path, otherwise
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-ruby requirements
74
+ ### Handling non-Ruby requirements
77
75
 
78
- Opal's `require` method is also special as it allows non-ruby source
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 javascript source files. Javascript sources are
81
- treated as first class citizens in Opal. The Opal gem also supports
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 ruby statements against constants
89
- that exist in all ruby runtimes, they will not affect any running code:
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
- if RUBY_ENGINE == 'opal'
92
- # this code compiles
93
- else
94
- # this code never compiles
95
- end
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
- unless RUBY_ENGINE == 'opal'
100
- # this code will not run
101
- end
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
- if RUBY_ENGINE != 'opal'
106
- puts "do not run this code"
107
- end
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 dont run at all at runtime, but they also never
110
- compile so will never be in the output javascript code. This is
111
- particularly useful for using code in both mri and Opal.
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 opal (and vice-versa 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 javascript output.
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 javascript/opal version of an app.
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
+
@@ -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
+
@@ -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.
@@ -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
+ ```