batch_api 0.1.1 → 0.2.0

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.
Files changed (81) hide show
  1. data/changelog.md +17 -0
  2. data/lib/batch_api.rb +6 -1
  3. data/lib/batch_api/batch_error.rb +41 -0
  4. data/lib/batch_api/configuration.rb +31 -21
  5. data/lib/batch_api/error_wrapper.rb +44 -0
  6. data/lib/batch_api/internal_middleware.rb +87 -0
  7. data/lib/batch_api/internal_middleware/decode_json_body.rb +24 -0
  8. data/lib/batch_api/internal_middleware/response_filter.rb +27 -0
  9. data/lib/batch_api/operation/rack.rb +4 -5
  10. data/lib/batch_api/processor.rb +22 -20
  11. data/lib/batch_api/processor/executor.rb +18 -0
  12. data/lib/batch_api/processor/sequential.rb +29 -0
  13. data/lib/batch_api/{middleware.rb → rack_middleware.rb} +2 -2
  14. data/lib/batch_api/response.rb +10 -8
  15. data/lib/batch_api/version.rb +1 -1
  16. data/readme.md +179 -106
  17. data/spec/dummy/README.rdoc +261 -0
  18. data/spec/dummy/Rakefile +15 -0
  19. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  20. data/spec/dummy/app/assets/javascripts/endpoints.js +2 -0
  21. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  22. data/spec/dummy/app/assets/stylesheets/endpoints.css +4 -0
  23. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  24. data/spec/dummy/app/controllers/endpoints_controller.rb +36 -0
  25. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  26. data/spec/dummy/app/helpers/endpoints_helper.rb +2 -0
  27. data/spec/dummy/app/views/endpoints/get.html.erb +2 -0
  28. data/spec/dummy/app/views/endpoints/post.html.erb +2 -0
  29. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  30. data/spec/dummy/config.ru +4 -0
  31. data/spec/dummy/config/application.rb +63 -0
  32. data/spec/dummy/config/boot.rb +10 -0
  33. data/spec/dummy/config/database.yml +25 -0
  34. data/spec/dummy/config/environment.rb +5 -0
  35. data/spec/dummy/config/environments/development.rb +37 -0
  36. data/spec/dummy/config/environments/production.rb +67 -0
  37. data/spec/dummy/config/environments/test.rb +37 -0
  38. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  39. data/spec/dummy/config/initializers/inflections.rb +15 -0
  40. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  41. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  42. data/spec/dummy/config/initializers/session_store.rb +8 -0
  43. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  44. data/spec/dummy/config/locales/en.yml +5 -0
  45. data/spec/dummy/config/routes.rb +64 -0
  46. data/spec/dummy/db/development.sqlite3 +0 -0
  47. data/spec/dummy/db/test.sqlite3 +0 -0
  48. data/spec/dummy/log/development.log +1742 -0
  49. data/spec/dummy/log/test.log +48237 -0
  50. data/spec/dummy/public/404.html +26 -0
  51. data/spec/dummy/public/422.html +26 -0
  52. data/spec/dummy/public/500.html +25 -0
  53. data/spec/dummy/public/favicon.ico +0 -0
  54. data/spec/dummy/script/rails +6 -0
  55. data/spec/dummy/test/functional/endpoints_controller_test.rb +14 -0
  56. data/spec/dummy/test/unit/helpers/endpoints_helper_test.rb +4 -0
  57. data/spec/integration/rails_spec.rb +10 -0
  58. data/spec/integration/shared_examples.rb +256 -0
  59. data/spec/integration/sinatra_integration_spec.rb +14 -0
  60. data/spec/lib/batch_api_spec.rb +20 -0
  61. data/spec/lib/batch_error_spec.rb +23 -0
  62. data/spec/lib/configuration_spec.rb +30 -0
  63. data/spec/lib/error_wrapper_spec.rb +68 -0
  64. data/spec/lib/internal_middleware/decode_json_body_spec.rb +37 -0
  65. data/spec/lib/internal_middleware/response_filter_spec.rb +61 -0
  66. data/spec/lib/internal_middleware_spec.rb +91 -0
  67. data/spec/lib/operation/rack_spec.rb +243 -0
  68. data/spec/lib/operation/rails_spec.rb +100 -0
  69. data/spec/lib/processor/executor_spec.rb +22 -0
  70. data/spec/lib/processor/sequential_spec.rb +39 -0
  71. data/spec/lib/processor_spec.rb +134 -0
  72. data/spec/lib/rack_middleware_spec.rb +103 -0
  73. data/spec/lib/response_spec.rb +53 -0
  74. data/spec/spec_helper.rb +28 -0
  75. data/spec/support/sinatra_app.rb +54 -0
  76. metadata +148 -12
  77. data/lib/batch_api/error.rb +0 -3
  78. data/lib/batch_api/errors/base.rb +0 -45
  79. data/lib/batch_api/errors/operation.rb +0 -7
  80. data/lib/batch_api/errors/request.rb +0 -26
  81. data/lib/batch_api/processor/strategies/sequential.rb +0 -18
@@ -0,0 +1,18 @@
1
+ module BatchApi
2
+ class Processor
3
+ # Public: a simple middleware that lives at the end of the internal chain
4
+ # and simply executes each batch operation.
5
+ class Executor
6
+
7
+ # Public: initialize the middleware.
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ # Public: execute the batch operation.
13
+ def call(env)
14
+ env[:op].execute
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ module BatchApi
2
+ class Processor
3
+ class Sequential
4
+ # Public: initialize with the app.
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ # Public: execute all operations sequentially.
10
+ #
11
+ # ops - a set of BatchApi::Operations
12
+ # options - a set of options
13
+ #
14
+ # Returns an array of BatchApi::Response objects.
15
+ def call(env)
16
+ env[:ops].collect do |op|
17
+ # set the current op
18
+ env[:op] = op
19
+
20
+ # execute the individual request inside the operation-specific
21
+ # middeware, then clear out the current op afterward
22
+ middleware = InternalMiddleware.operation_stack
23
+ middleware.call(env).tap {|r| env.delete(:op) }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -1,5 +1,5 @@
1
1
  module BatchApi
2
- class Middleware
2
+ class RackMiddleware
3
3
  def initialize(app, &block)
4
4
  @app = app
5
5
  yield BatchApi.config if block
@@ -12,7 +12,7 @@ module BatchApi
12
12
  result = BatchApi::Processor.new(request, @app).execute!
13
13
  [200, self.class.content_type, [MultiJson.dump(result)]]
14
14
  rescue => err
15
- BatchApi::Errors::Request.new(err).render
15
+ ErrorWrapper.new(err).render
16
16
  end
17
17
  else
18
18
  @app.call(env)
@@ -1,5 +1,3 @@
1
- require 'batch_api/error'
2
-
3
1
  module BatchApi
4
2
  # Public: a response from an internal operation in the Batch API.
5
3
  # It contains all the details that are needed to describe the call's
@@ -15,6 +13,15 @@ module BatchApi
15
13
  @body = process_body(response[2])
16
14
  end
17
15
 
16
+ # Public: convert the response to JSON. nil values are ignored.
17
+ def as_json(options = {})
18
+ {}.tap do |result|
19
+ result[:body] = @body unless @body.nil?
20
+ result[:headers] = @headers unless @headers.nil?
21
+ result[:status] = @status unless @status.nil?
22
+ end
23
+ end
24
+
18
25
  private
19
26
 
20
27
  def process_body(body_pieces)
@@ -24,12 +31,7 @@ module BatchApi
24
31
  # so turn it into a string
25
32
  base_body = ""
26
33
  body_pieces.each {|str| base_body << str}
27
- should_decode? ? MultiJson.load(base_body) : base_body
28
- end
29
-
30
- def should_decode?
31
- @headers["Content-Type"] =~ /^application\/json/ &&
32
- BatchApi.config.decode_json_responses
34
+ base_body
33
35
  end
34
36
  end
35
37
  end
@@ -1,3 +1,3 @@
1
1
  module BatchApi
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/readme.md CHANGED
@@ -1,7 +1,52 @@
1
- A proposal for a Batch API endpoint.
1
+ [![Build Status](https://secure.travis-ci.org/arsduo/batch_api.png?branch=master)](http://travis-ci.org/arsduo/batch_api)
2
2
 
3
- Batch requests take the form of a series of REST API requests,
4
- each containing the following arguments:
3
+ ## What's this?
4
+
5
+ A gem that provides a RESTful Batch API for Rails and other Rack applications.
6
+ In this system, batch requests are simply collections of regular REST calls,
7
+ whose results are returned as an equivalent collection of regular REST results.
8
+
9
+ This is heavily inspired by [Facebook's Batch API](http://developers.facebook.com/docs/reference/api/batch/).
10
+
11
+ ## A Quick Example
12
+
13
+ Making a batch request:
14
+
15
+ ```
16
+ # POST /batch
17
+ # Content-Type: application/json
18
+
19
+ {
20
+ ops: [
21
+ {method: "get", url: "/patrons"},
22
+ {method: "post", url: "/orders/new", params: {dish_id: 123}},
23
+ {method: "get", url: "/oh/no/error", headers: {break: "fast"}},
24
+ {method: "delete", url: "/patrons/456"}
25
+ ],
26
+ sequential: true
27
+ }
28
+ ```
29
+
30
+ Reading the response:
31
+
32
+ ```
33
+ {
34
+ results: [
35
+ {status: 200, body: [{id: 1, name: "Jim-Bob"}, ...], headers: {}},
36
+ {status: 201, body: {id: 4, dish_name: "Spicy Crab Legs"}, headers: {}},
37
+ {status: 500, body: {error: {oh: "noes!"}}, headers: {Problem: "woops"}},
38
+ {status: 200, body: null, headers: {}}}
39
+ ]
40
+ }
41
+ ```
42
+
43
+ ### How It Works
44
+
45
+ #### Requests
46
+
47
+ As you can see from the example above, each request in the batch (an
48
+ "operation", in batch parlance) describes the same features any HTTP request
49
+ would include:
5
50
 
6
51
  * _url_ - the API endpoint to hit, formatted exactly as you would for a regular
7
52
  REST API request (e.g. leading /, etc.)
@@ -9,137 +54,165 @@ REST API request (e.g. leading /, etc.)
9
54
  * _args_ - a hash of arguments to the API. This can be used for both GET and
10
55
  PUT/POST/PATCH requests.
11
56
  * _headers_ - a hash of request-specific headers. (The headers sent in the
12
- request will be included as well, with request-specific headers taking
57
+ request will be included as well, with operation-specific headers taking
13
58
  precendence.)
14
- * _options_ - a hash of additional batch request options. There are currently
15
- none supported, but we plan to introduce some for dependency management,
16
- supressing output, etc. in the future.
17
59
 
18
- The Batch API endpoint itself (which lives at POST /batch) takes the
19
- following arguments:
60
+ These individual operations are supplied as the "ops" parameter in the
61
+ overall request. Other options include:
20
62
 
21
- * _ops_ - an array of operations to perform, specified as described above.
22
63
  * _sequential_ - execute all operations sequentially, rather than in parallel.
23
- *THIS PARAMETER IS CURRENTLY REQUIRED AND MUST BE SET TO TRUE.* (In the future
24
- we'll offer parallel processing by default, and hence this parameter must be
25
- supplied in order topreserve expected behavior.
64
+ *This parameter is currently REQUIRED and must be set to true.* (In the future
65
+ the Batch API will offer parallel processing for thread-safe apps, and hence
66
+ this parameter must be supplied in order to explicitly preserve expected
67
+ behavior.)
26
68
 
27
- Other options may be defined in the future.
69
+ Other options may be provided in the future for both the global request
70
+ and individual operations.
28
71
 
29
- Users must be logged in to use the Batch API.
72
+ ### Responses
30
73
 
31
- The Batch API returns an array of results in the same order the operations are
32
- specified. Each result contains:
74
+ The Batch API will always return a 200, with a JSON body containing the
75
+ individual responses under the "results" key. Those responses, in turn,
76
+ contain the same main components of any HTTP response:
33
77
 
34
78
  * _status_ - the HTTP status (200, 201, 400, etc.)
35
79
  * _body_ - the rendered body
36
80
  * _headers_ - any response headers
37
- * _cookies_ - any cookies set by the request. (These will in the future be
38
- pulled into the main response to be processed by the client.)
81
+
82
+ ### Errors
39
83
 
40
84
  Errors in individual Batch API requests will be returned inline, with the
41
- same status code and body they would return as individual requests. If the
42
- Batch API itself returns a non-200 status code, that indicates a global
43
- problem:
85
+ same status code and body they would return as individual requests.
44
86
 
45
- * _403_ - if the user isn't logged in
46
- * _422_ - if the batch request isn't properly formatted
47
- * _500_ - if there's an application error in the Batch API code
87
+ If the Batch API itself returns a non-200 status code, that indicates a global
88
+ problem.
48
89
 
49
- ** Examples **
90
+ ## Why a Batch API?
50
91
 
51
- Given the following request:
92
+ Batch APIs, though unRESTful, are useful for reducing HTTP overhead
93
+ by combining requests; this is particularly valuable for mobile clients,
94
+ which may generate groups of offline actions and which desire to
95
+ reduce battery consumption while connected by making fewer, better-compressed
96
+ requests.
52
97
 
53
- ```ruby
54
- {
55
- ops: [
56
- {
57
- method: "post",
58
- url: "/resource/create",
59
- args: {title: "bar", data: "foo"}
60
- },
61
- {
62
- method: "get",
63
- url: "/other_resource/123/connections"
64
- },
65
- {
66
- method: "get",
67
- url: "/i/gonna/throw/an/error",
68
- header: { some: "headers" }
69
- }
70
- ]
71
- }
72
- ```
98
+ ### Why not HTTP Pipelining?
99
+
100
+ HTTP pipelining is an awesome and promising technology, and would provide a
101
+ simple and effortless way to parallel process many requests; however, using
102
+ pipelining raised several issues for us, one of which was a blocker:
103
+
104
+ * [Lack of browser
105
+ support](http://en.wikipedia.org/wiki/HTTP_pipelining#Implementation_in_web_browsers):
106
+ a number of key browsers do not yet support HTTP pipelining (or have it
107
+ disabled by default). This will of course change in time,
108
+ but for now this takes pipelining out of consideration. (There a similar but
109
+ more minor issue
110
+ with [many web
111
+ proxies](http://en.wikipedia.org/wiki/HTTP_pipelining#Implementation_in_web_proxies).)
112
+ * The HTTP pipelining specification states that non-idempotent requests (e.g.
113
+ [POST](http://en.wikipedia.org/wiki/HTTP_pipelining) and
114
+ [in some
115
+ descriptions](http://www-archive.mozilla.org/projects/netlib/http/pipelining-faq.html) PUT)
116
+ shouldn't be made via pipelining. Though I have heard that some server
117
+ implementations do support POST requests (putting all subsequent requests on
118
+ hold until it's done), for applications that submit a lot of POSTs this raised
119
+ concerns as well.
120
+
121
+ Given this state of affairs -- and my desire to hack up a Batch API gem :P --,
122
+ we decided to implement an API-based solution.
123
+
124
+ ### Why this Approach?
125
+
126
+ There are two main approaches to writing batch APIs:
127
+
128
+ * A limited, specialized batch endpoint (or endpoints), which usually handles
129
+ updates and creates. DHH sketched out such a bulk update/create endpoint
130
+ for Rails 3.2 [in a gist](https://gist.github.com/981520) last year.
131
+ * A general-purpose RESTful API that can handle anything in your application,
132
+ a la the Facebook Batch API.
133
+
134
+ The second approach, IMO, minimizes code duplication and complexity. Rather
135
+ than have two systems that manage resources (or a more complicated one that
136
+ can handle both batch and individual requests), we simply route requests as we
137
+ always would.
73
138
 
74
- You'd get the following back:
139
+ This solution has several specific benefits:
75
140
 
76
- ```ruby
77
- [
78
- {status: 201, body: "{json:\"data\"}", headers: {}, cookies: {}},
79
- {status: 200, body: "[{json:\"data\"}, {more:\"data\"}]", headers: {}, cookies: {}},
80
- {status: 500, body: "{error:\"message\"}", headers: {}, cookies: {}},
81
- ]
82
- ```
141
+ * Less complexity - non-batch endpoints don't need any extra code, which means
142
+ less to maintain on your end.
143
+ * Complete flexibility - as you add new features to your application,
144
+ they become immediately and automatically available via the Batch API.
145
+ * More RESTful - as individual operations are simply actions on RESTful
146
+ resources, you preserve an important characteristic of your API.
83
147
 
84
- ** Implementation**
148
+ As well as the general benefits of all batch operations:
85
149
 
86
- For each request, we:
87
- * attempt to route it as Rails would (identifying controller and action)
88
- * create a customized request.env hash with the appropriate details
89
- * instantiate the controller and invoke the action
90
- * parse and process the result
150
+ * Reuse of state - user authentication, request stack processing, and
151
+ similar processing only needs to be done once.
152
+ * Better for clients - clients need to make fewer requests, as described above.
153
+ * Parallelizable - in the future, we could run requests in parallel (if
154
+ our app is thread-safe). Clients would be able to explicitly specify
155
+ dependencies between operations (or simply run all sequentially). This
156
+ should make for some fun experimentation :)
91
157
 
92
- The overall result is then returned to the client.
158
+ There's only one downside I can think of to this approach as opposed to a
159
+ specialized endpoint:
93
160
 
94
- **Background**
161
+ * Reduced ability to optimize - unlike a specialized API endpoint, each request
162
+ will be treated in isolation, which makes it harder to optimize the
163
+ underlying database queries via more efficient (read: complicated) SQL logic.
164
+ (Better identity maps would help with this, and since the main pain point
165
+ this approach addresses is at the HTTP connection layer, I submit we can
166
+ accept this.)
95
167
 
96
- Batch APIs, though unRESTful, are useful for reducing HTTP overhead
97
- by combining requests; this is particularly valuable for mobile clients,
98
- which may generate groups of offline actions and which desire to
99
- reduce battery consumption while connected by making fewer, better-compressed
100
- requests.
168
+ ## Implementation
101
169
 
102
- Generally, such interfaces fall into two categories:
170
+ The Batch API is implemented as a Rack middleware. Here's how it works:
103
171
 
104
- * a set of limited, specialized instructions, usually to manage resources
105
- * a general-purpose API that can take any operation the main API can
106
- handle
172
+ First, if the request isn't a batch request (as defined by the endpoint and
173
+ method in BatchApi.config), it gets processed normally by your app.
107
174
 
108
- The second approach minimizes code duplication and complexity. Rather than
109
- have two systems that manage resources (or a more complicated one that can
110
- handle both batch and individual requests), we simply route requests as we
111
- always would.
175
+ If it is a batch request, we:
176
+ * Read and validate the parameters for the request, constructing a
177
+ representation of the operation.
178
+ * Compile a customized Rack environment hash with the appropriate parameters,
179
+ so that your app interprets the request as being for the appropriate action.
180
+ (This is requires a bit of extra processing for Rails.)
181
+ * Send each request up the middleware stack as normal, collecting the results.
182
+ Errors are caught and recorded appropriately.
183
+ * Send you back the results.
112
184
 
113
- This approach has several benefits:
185
+ At both the batch level (processing all requests) and the individual operation
186
+ request, there is an internal, customizable midleware stack that you can
187
+ customize to insert additional custom behavior, such as handling authentication
188
+ or decoding JSON bodies for individual requests (this latter comes
189
+ pre-included). Check out the lib/batch_api/internal_middleware.rb for more
190
+ information.
114
191
 
115
- * Less complexity - non-batch endpoints don't need any extra code
116
- * Complete flexibility - as we add new features or endpoints to the API,
117
- they become immediately available via the Batch API.
118
- * More RESTful - as individual operations are simply actions on RESTful
119
- resources, we preserve an important characteristic of the API.
192
+ ## To Do
120
193
 
121
- As well as general benefits of using the Batch API:
194
+ The core of the Batch API is complete and solid, and so ready to go that it's
195
+ in use at 6Wunderkinder already :P
122
196
 
123
- * Parallelizable - in the future, we could run requests in parallel (if
124
- our Rails app is running in thread-safe mode), allowing clients to
125
- specify explicit dependencies between operations (or run all
126
- sequentially).
127
- * Reuse of state - user authentication, request stack processing, and
128
- similar processing only needs to be done once.
129
- * Better for clients - fewer requests, better compressibility, etc.
130
- (as described above)
131
-
132
- There are two main downsides to our implementation:
133
-
134
- * Rails dependency - we use only public Rails interfaces, but these could
135
- still change with major updates. (_Resolution:_ with good testing we
136
- can identify changes and update code as needed.)
137
- * Reduced ability to optimize cross-request - unlike a specialized API,
138
- each request will be treated in isolation, and so you couldn't minimize
139
- DB updates through more complicated SQL logic. (_Resolution:_ none, but
140
- the main pain point currently is at the HTTP connection layer, so we
141
- accept this.)
142
-
143
- Once the Batch API is more developed, we'll spin it off into a gem, and
144
- possibly make it easy to create versions for Sinatra or other frameworks,
145
- if desired.
197
+ Here are some immediate tasks:
198
+
199
+ * Test against additional frameworks (beyond Rails and Sinatra)
200
+ * Write more usage docs / create a wiki.
201
+ * Add additional features inspired by the Facebook API, such as the ability to
202
+ surpress output for individual requests, etc.
203
+ * Add RDoc to the spec task and ensure all methods are documented.
204
+ * Research and implement parallelization and dependency management.
205
+
206
+ ## Thanks
207
+
208
+ To 6Wunderkinder, for all their support for this open-source project, and their
209
+ general awesomeness.
210
+
211
+ To Facebook, for providing inspiration and a great implementation in this and
212
+ many other things.
213
+
214
+ To [JT Archie](http://github.com/jtarchie) for his help and feedback.
215
+
216
+ ## Issues? Questions? Ideas?
217
+
218
+ Open a ticket or send a pull request!
@@ -0,0 +1,261 @@
1
+ == Welcome to Rails
2
+
3
+ Rails is a web-application framework that includes everything needed to create
4
+ database-backed web applications according to the Model-View-Control pattern.
5
+
6
+ This pattern splits the view (also called the presentation) into "dumb"
7
+ templates that are primarily responsible for inserting pre-built data in between
8
+ HTML tags. The model contains the "smart" domain objects (such as Account,
9
+ Product, Person, Post) that holds all the business logic and knows how to
10
+ persist themselves to a database. The controller handles the incoming requests
11
+ (such as Save New Account, Update Product, Show Post) by manipulating the model
12
+ and directing data to the view.
13
+
14
+ In Rails, the model is handled by what's called an object-relational mapping
15
+ layer entitled Active Record. This layer allows you to present the data from
16
+ database rows as objects and embellish these data objects with business logic
17
+ methods. You can read more about Active Record in
18
+ link:files/vendor/rails/activerecord/README.html.
19
+
20
+ The controller and view are handled by the Action Pack, which handles both
21
+ layers by its two parts: Action View and Action Controller. These two layers
22
+ are bundled in a single package due to their heavy interdependence. This is
23
+ unlike the relationship between the Active Record and Action Pack that is much
24
+ more separate. Each of these packages can be used independently outside of
25
+ Rails. You can read more about Action Pack in
26
+ link:files/vendor/rails/actionpack/README.html.
27
+
28
+
29
+ == Getting Started
30
+
31
+ 1. At the command prompt, create a new Rails application:
32
+ <tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
33
+
34
+ 2. Change directory to <tt>myapp</tt> and start the web server:
35
+ <tt>cd myapp; rails server</tt> (run with --help for options)
36
+
37
+ 3. Go to http://localhost:3000/ and you'll see:
38
+ "Welcome aboard: You're riding Ruby on Rails!"
39
+
40
+ 4. Follow the guidelines to start developing your application. You can find
41
+ the following resources handy:
42
+
43
+ * The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
44
+ * Ruby on Rails Tutorial Book: http://www.railstutorial.org/
45
+
46
+
47
+ == Debugging Rails
48
+
49
+ Sometimes your application goes wrong. Fortunately there are a lot of tools that
50
+ will help you debug it and get it back on the rails.
51
+
52
+ First area to check is the application log files. Have "tail -f" commands
53
+ running on the server.log and development.log. Rails will automatically display
54
+ debugging and runtime information to these files. Debugging info will also be
55
+ shown in the browser on requests from 127.0.0.1.
56
+
57
+ You can also log your own messages directly into the log file from your code
58
+ using the Ruby logger class from inside your controllers. Example:
59
+
60
+ class WeblogController < ActionController::Base
61
+ def destroy
62
+ @weblog = Weblog.find(params[:id])
63
+ @weblog.destroy
64
+ logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
65
+ end
66
+ end
67
+
68
+ The result will be a message in your log file along the lines of:
69
+
70
+ Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
71
+
72
+ More information on how to use the logger is at http://www.ruby-doc.org/core/
73
+
74
+ Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
75
+ several books available online as well:
76
+
77
+ * Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
78
+ * Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
79
+
80
+ These two books will bring you up to speed on the Ruby language and also on
81
+ programming in general.
82
+
83
+
84
+ == Debugger
85
+
86
+ Debugger support is available through the debugger command when you start your
87
+ Mongrel or WEBrick server with --debugger. This means that you can break out of
88
+ execution at any point in the code, investigate and change the model, and then,
89
+ resume execution! You need to install ruby-debug to run the server in debugging
90
+ mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:
91
+
92
+ class WeblogController < ActionController::Base
93
+ def index
94
+ @posts = Post.all
95
+ debugger
96
+ end
97
+ end
98
+
99
+ So the controller will accept the action, run the first line, then present you
100
+ with a IRB prompt in the server window. Here you can do things like:
101
+
102
+ >> @posts.inspect
103
+ => "[#<Post:0x14a6be8
104
+ @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>,
105
+ #<Post:0x14a6620
106
+ @attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
107
+ >> @posts.first.title = "hello from a debugger"
108
+ => "hello from a debugger"
109
+
110
+ ...and even better, you can examine how your runtime objects actually work:
111
+
112
+ >> f = @posts.first
113
+ => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
114
+ >> f.
115
+ Display all 152 possibilities? (y or n)
116
+
117
+ Finally, when you're ready to resume execution, you can enter "cont".
118
+
119
+
120
+ == Console
121
+
122
+ The console is a Ruby shell, which allows you to interact with your
123
+ application's domain model. Here you'll have all parts of the application
124
+ configured, just like it is when the application is running. You can inspect
125
+ domain models, change values, and save to the database. Starting the script
126
+ without arguments will launch it in the development environment.
127
+
128
+ To start the console, run <tt>rails console</tt> from the application
129
+ directory.
130
+
131
+ Options:
132
+
133
+ * Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
134
+ made to the database.
135
+ * Passing an environment name as an argument will load the corresponding
136
+ environment. Example: <tt>rails console production</tt>.
137
+
138
+ To reload your controllers and models after launching the console run
139
+ <tt>reload!</tt>
140
+
141
+ More information about irb can be found at:
142
+ link:http://www.rubycentral.org/pickaxe/irb.html
143
+
144
+
145
+ == dbconsole
146
+
147
+ You can go to the command line of your database directly through <tt>rails
148
+ dbconsole</tt>. You would be connected to the database with the credentials
149
+ defined in database.yml. Starting the script without arguments will connect you
150
+ to the development database. Passing an argument will connect you to a different
151
+ database, like <tt>rails dbconsole production</tt>. Currently works for MySQL,
152
+ PostgreSQL and SQLite 3.
153
+
154
+ == Description of Contents
155
+
156
+ The default directory structure of a generated Ruby on Rails application:
157
+
158
+ |-- app
159
+ | |-- assets
160
+ | |-- images
161
+ | |-- javascripts
162
+ | `-- stylesheets
163
+ | |-- controllers
164
+ | |-- helpers
165
+ | |-- mailers
166
+ | |-- models
167
+ | `-- views
168
+ | `-- layouts
169
+ |-- config
170
+ | |-- environments
171
+ | |-- initializers
172
+ | `-- locales
173
+ |-- db
174
+ |-- doc
175
+ |-- lib
176
+ | `-- tasks
177
+ |-- log
178
+ |-- public
179
+ |-- script
180
+ |-- test
181
+ | |-- fixtures
182
+ | |-- functional
183
+ | |-- integration
184
+ | |-- performance
185
+ | `-- unit
186
+ |-- tmp
187
+ | |-- cache
188
+ | |-- pids
189
+ | |-- sessions
190
+ | `-- sockets
191
+ `-- vendor
192
+ |-- assets
193
+ `-- stylesheets
194
+ `-- plugins
195
+
196
+ app
197
+ Holds all the code that's specific to this particular application.
198
+
199
+ app/assets
200
+ Contains subdirectories for images, stylesheets, and JavaScript files.
201
+
202
+ app/controllers
203
+ Holds controllers that should be named like weblogs_controller.rb for
204
+ automated URL mapping. All controllers should descend from
205
+ ApplicationController which itself descends from ActionController::Base.
206
+
207
+ app/models
208
+ Holds models that should be named like post.rb. Models descend from
209
+ ActiveRecord::Base by default.
210
+
211
+ app/views
212
+ Holds the template files for the view that should be named like
213
+ weblogs/index.html.erb for the WeblogsController#index action. All views use
214
+ eRuby syntax by default.
215
+
216
+ app/views/layouts
217
+ Holds the template files for layouts to be used with views. This models the
218
+ common header/footer method of wrapping views. In your views, define a layout
219
+ using the <tt>layout :default</tt> and create a file named default.html.erb.
220
+ Inside default.html.erb, call <% yield %> to render the view using this
221
+ layout.
222
+
223
+ app/helpers
224
+ Holds view helpers that should be named like weblogs_helper.rb. These are
225
+ generated for you automatically when using generators for controllers.
226
+ Helpers can be used to wrap functionality for your views into methods.
227
+
228
+ config
229
+ Configuration files for the Rails environment, the routing map, the database,
230
+ and other dependencies.
231
+
232
+ db
233
+ Contains the database schema in schema.rb. db/migrate contains all the
234
+ sequence of Migrations for your schema.
235
+
236
+ doc
237
+ This directory is where your application documentation will be stored when
238
+ generated using <tt>rake doc:app</tt>
239
+
240
+ lib
241
+ Application specific libraries. Basically, any kind of custom code that
242
+ doesn't belong under controllers, models, or helpers. This directory is in
243
+ the load path.
244
+
245
+ public
246
+ The directory available for the web server. Also contains the dispatchers and the
247
+ default HTML files. This should be set as the DOCUMENT_ROOT of your web
248
+ server.
249
+
250
+ script
251
+ Helper scripts for automation and generation.
252
+
253
+ test
254
+ Unit and functional tests along with fixtures. When using the rails generate
255
+ command, template test files will be generated for you and placed in this
256
+ directory.
257
+
258
+ vendor
259
+ External libraries that the application depends on. Also includes the plugins
260
+ subdirectory. If the app has frozen rails, those gems also go here, under
261
+ vendor/rails/. This directory is in the load path.