From 396d599e24cbfa15c62b20fab8cc02cdba046ce3 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 6 Nov 2008 01:10:30 +0530 Subject: Update guides from docrails --- railties/doc/guides/html/2_2_release_notes.html | 2 +- .../doc/guides/html/actioncontroller_basics.html | 299 +++++++++++++++------ .../html/activerecord_validations_callbacks.html | 267 ++++++++++++++++++ railties/doc/guides/html/authors.html | 13 + .../guides/html/debugging_rails_applications.html | 192 ++++++++++--- railties/doc/guides/html/finders.html | 45 ++-- railties/doc/guides/html/form_helpers.html | 76 +++++- .../guides/html/getting_started_with_rails.html | 114 ++++---- railties/doc/guides/html/index.html | 20 +- railties/doc/guides/html/migrations.html | 2 +- .../guides/html/testing_rails_applications.html | 67 ++--- 11 files changed, 839 insertions(+), 258 deletions(-) create mode 100644 railties/doc/guides/html/activerecord_validations_callbacks.html (limited to 'railties/doc/guides/html') diff --git a/railties/doc/guides/html/2_2_release_notes.html b/railties/doc/guides/html/2_2_release_notes.html index c68f10ad5a..931786ef6c 100644 --- a/railties/doc/guides/html/2_2_release_notes.html +++ b/railties/doc/guides/html/2_2_release_notes.html @@ -407,7 +407,7 @@ More information :

All told, the Guides provide tens of thousands of words of guidance for beginning and intermediate Rails developers.

-

If you want to these generate guides locally, inside your application:

+

If you want to generate these guides locally, inside your application:

-
class ClientsController < ActionController::Base
+
class ClientsController < ApplicationController
 
   # Actions are public methods
   def new
@@ -321,7 +374,7 @@ private
 end
 

Private methods in a controller are also used as filters, which will be covered later in this guide.

-

As an example, if the user goes to /clients/new in your application to add a new client, a ClientsController instance will be created and the new method will be run. Note that the empty method from the example above could work just fine because Rails will by default render the new.html.erb view unless the action says otherwise. The new method could make available to the view a @client instance variable by creating a new Client:

+

As an example, if the user goes to /clients/new in your application to add a new client, Rails will create a ClientsController instance will be created and run the new method. Note that the empty method from the example above could work just fine because Rails will by default render the new.html.erb view unless the action says otherwise. The new method could make available to the view a @client instance variable by creating a new Client:

end

The Layouts & rendering guide explains this in more detail.

+

ApplicationController inherits from ActionController::Base, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the API documentation or in the source itself.

3. Parameters

-

You will probably want to access data sent in by the user or other parameters in your controller actions. There are two kinds of parameters possible in a web application. The first are parameters that are sent as part of the URL, query string parameters. The query string is everything after "?" in the URL. The second type of parameter is usually referred to as POST data. This information usually comes from a HTML form which has been filled in by the user. It's called POST data because it can only be sent as part of an HTTP POST request. Rails does not make any distinction between query string parameters and POST parameters, and both are available in the params hash in your controller:

+

You will probably want to access data sent in by the user or other parameters in your controller actions. There are two kinds of parameters possible in a web application. The first are parameters that are sent as part of the URL, called query string parameters. The query string is everything after "?" in the URL. The second type of parameter is usually referred to as POST data. This information usually comes from a HTML form which has been filled in by the user. It's called POST data because it can only be sent as part of an HTTP POST request. Rails does not make any distinction between query string parameters and POST parameters, and both are available in the params hash in your controller:

class ClientsController < ActionController::Base
 
-  # This action uses query string parameters because it gets run by a HTTP GET request,
-  # but this does not make any difference to the way in which the parameters are accessed.
-  # The URL for this action would look like this in order to list activated clients: /clients?status=activated
+  # This action uses query string parameters because it gets run by a HTTP
+  # GET request, but this does not make any difference to the way in which
+  # the parameters are accessed. The URL for this action would look like this
+  # in order to list activated clients: /clients?status=activated
   def index
     if params[:status] = "activated"
       @clients = Client.activated
@@ -370,11 +425,11 @@ http://www.gnu.org/software/src-highlite -->
 
 end
 
-

3.1. Hash and array parameters

+

3.1. Hash and Array Parameters

The params hash is not limited to one-dimensional keys and values. It can contain arrays and (nested) hashes. To send an array of values, append "[]" to the key name:

-
GET /clients?ids[]=1&ids[2]&ids[]=3
+
GET /clients?ids[]=1&ids[]=2&ids[]=3

The value of params[:ids] will now be ["1", "2", "3"]. Note that parameter values are always strings; Rails makes no attempt to guess or cast the type.

To send a hash you include the key name inside the brackets:

@@ -388,8 +443,32 @@ http://www.gnu.org/software/src-highlite --> </form>

The value of params[:client] when this form is submitted will be {:name ⇒ "Acme", :phone ⇒ "12345", :address ⇒ {:postcode ⇒ "12345", :city ⇒ "Carrot City"}}. Note the nested hash in params[:client][:address].

-

3.2. Routing parameters

-

The params hash will always contain the :controller and :action keys, but you should use the methods controller_name and action_name instead to access these values. Any other parameters defined by the routing, such as :id will also be available.

+

3.2. Routing Parameters

+

The params hash will always contain the :controller and :action keys, but you should use the methods controller_name and action_name instead to access these values. Any other parameters defined by the routing, such as :id will also be available. As an example, consider a listing of clients where the list can show either active or inactive clients. We can add a route which captures the :status parameter in a "pretty" URL:

+
+
+
# ...
+map.connect "/clients/:status", :controller => "clients", :action => "index", :foo => "bar"
+# ...
+
+

In this case, when a user opens the URL /clients/active, params[:status] will be set to "active". When this route is used, params[:foo] will also be set to "bar" just like it was passed in the query string in the same way params[:action] will contain "index".

+

3.3. default_url_options

+

You can set global default parameters that will be used when generating URLs with default_url_options. To do this, define a method with that name in your controller:

+
+
+
class ApplicationController < ActionController::Base
+
+  #The options parameter is the hash passed in to url_for
+  def default_url_options(options)
+    {:locale => I18n.locale}
+  end
+
+end
+
+

These options will be used as a starting-point when generating, so it's possible they'll be overridden by url_for. Because this method is defined in the controller, you can define it on ApplicationController so it would be used for all URL generation, or you could define it on only one controller for all URLs generated there.

4. Session

@@ -416,8 +495,9 @@ ActiveRecordStore - Stores the data in a database using Active Record.

-

All session stores store the session id in a cookie - there is no other way of passing it to the server. Most stores also use this key to locate the session data on the server.

-

The default and recommended store, the Cookie Store, does not store session data on the server, but in the cookie itself. The data is cryptographically signed to make it tamper-proof, but it is not encrypted, so anyone with access to it can read its contents. It can only store about 4kB of data - much less than the others - but this is usually enough. Storing large amounts of data is discouraged no matter which session store your application uses. Expecially discouraged is storing complex objects (anything other than basic Ruby objects, the primary example being model instances) in the session, as the server might not be able to reassemble them between requests, which will result in an error. The Cookie Store has the added advantage that it does not require any setting up beforehand - Rails will generate a "secret key" which will be used to sign the cookie when you create the application.

+

All session stores store either the session ID or the entire session in a cookie - Rails does not allow the session ID to be passed in any other way. Most stores also use this key to locate the session data on the server.

+

The default and recommended store, the Cookie Store, does not store session data on the server, but in the cookie itself. The data is cryptographically signed to make it tamper-proof, but it is not encrypted, so anyone with access to it can read its contents but not edit it. It can only store about 4kB of data - much less than the others - but this is usually enough. Storing large amounts of data is discouraged no matter which session store your application uses. You should especially avoid storing complex objects (anything other than basic Ruby objects, the primary example being model instances) in the session, as the server might not be able to reassemble them between requests, which will result in an error. The Cookie Store has the added advantage that it does not require any setting up beforehand - Rails will generate a "secret key" which will be used to sign the cookie when you create the application.

+

Read more about session storage in the Security Guide.

If you need a different session storage mechanism, you can change it in the config/environment.rb file:

# Set to one of [:active_record_store, :drb_store, :mem_cache_store, :cookie_store]
 config.action_controller.session_store = :active_record_store
 
-

4.1. Disabling the session

-

Sometimes you don't need a session, and you can turn it off to avoid the unnecessary overhead. To do this, use the session class method in your controller:

+

4.1. Disabling the Session

+

Sometimes you don't need a session. In this case, you can turn it off to avoid the unnecessary overhead. To do this, use the session class method in your controller:

session :on end
-

Or even a single action:

+

Or even for specified actions:

session :on, :only => [:create, :update] end
-

4.2. Accessing the session

+

4.2. Accessing the Session

In your controller you can access the session through the session instance method.

@@ -481,7 +561,7 @@ http://www.gnu.org/software/src-highlite --> private # Finds the User with the ID stored in the session with the key :current_user_id - # This is a common way to do user login in a Rails application; logging in sets the + # This is a common way to handle user login in a Rails application; logging in sets the# session value and logging out removes it.def current_user @_current_user||= session[:current_user_id]&& User.find(session[:current_user_id]) @@ -525,9 +605,9 @@ http://www.gnu.org/software/src-highlite --> end -

To reset the entire session, use reset_session.

+

To reset the entire session, use reset_session.

4.3. The flash

-

The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for storing error messages etc. It is accessed in much the same way as the session, like a hash. Let's use the act of logging out as an example. The controller can set a message which will be displayed to the user on the next request:

+

The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for storing error messages etc. It is accessed in much the same way as the session, like a hash. Let's use the act of logging out as an example. The controller can send a message which will be displayed to the user on the next request:

end
-

4.3.1. flash.now

+

4.3.1. flash.now

By default, adding values to the flash will make them available to the next request, but sometimes you may want to access those values in the same request. For example, if the create action fails to save a resource and you render the new template directly, that's not going to result in a new request, but you may still want to display a message using the flash. To do this, you can use flash.now in the same way you use the normal flash:

end
-

Note that while for session values, you set the key to nil, to delete a cookie value, you use cookies.delete(:key).

+

Note that while for session values, you set the key to nil, to delete a cookie value, you should use cookies.delete(:key).

6. Filters

-

Filters are methods that are run before, after or "around" a controller action. For example, one filter might check to see if the logged in user has the right credentials to access that particular controller or action. Filters are inherited, so if you set a filter on ApplicationController, it will be run on every controller in your application. A common, simple filter is one which requires that a user is logged in for an action to be run. Let's define the filter method first:

+

Filters are methods that are run before, after or "around" a controller action. For example, one filter might check to see if the logged in user has the right credentials to access that particular controller or action. Filters are inherited, so if you set a filter on ApplicationController, it will be run on every controller in your application. A common, simple filter is one which requires that a user is logged in for an action to be run. You can define the filter method this way:

end
-

In this example, the filter is added to ApplicationController and thus all controllers in the application. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this, so to prevent this filter from running you can use skip_before_filter :

+

In this example, the filter is added to ApplicationController and thus all controllers in the application. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with skip_before_filter :

end
-

Now, the LoginsController's "new" and "create" actions will work as before without requiring the user to be logged in. The :only option is used to only skip this filter for these actions, and there is also an :except option which works the other way. These options can be used when adding filters too, so you can add a filter which only runs for selected actions in the first place.

-

6.1. After filters and around filters

+

Now, the LoginsController's "new" and "create" actions will work as before without requiring the user to be logged in. The :only option is used to only skip this filter for these actions, and there is also an :except option which works the other way. These options can be used when adding filters too, so you can add a filter which only runs for selected actions in the first place.

+

6.1. After Filters and Around Filters

In addition to the before filters, you can run filters after an action has run or both before and after. The after filter is similar to the before filter, but because the action has already been run it has access to the response data that's about to be sent to the client. Obviously, after filters can not stop the action from running. Around filters are responsible for running the action, but they can choose not to, which is the around filter's way of stopping it.

end

Note that the filter in this case uses send because the logged_in? method is private and the filter is not run in the scope of the controller. This is not the recommended way to implement this particular filter, but in more simple cases it might be useful.

-

The second way is to use a class (actually, any object that responds to the right methods will do) to handle the filtering. This is useful in cases that are more complex than can not be implemented in a readable and reusable way using the two other methods. As an example, we will rewrite the login filter again to use a class:

+

The second way is to use a class (actually, any object that responds to the right methods will do) to handle the filtering. This is useful in cases that are more complex than can not be implemented in a readable and reusable way using the two other methods. As an example, you could rewrite the login filter again to use a class:

end
-

Again, this is not an ideal example for this filter, because it's not run in the scope of the controller but gets it passed as an argument. The filter class has a class method filter which gets run before or after the action, depending on if it's a before or after filter. Classes used as around filters can also use the same filter method, which will get run in the same way. The method must yield to execute the action. Alternatively, it can have both a before and an after method that are run before and after the action.

+

Again, this is not an ideal example for this filter, because it's not run in the scope of the controller but gets the controller passed as an argument. The filter class has a class method filter which gets run before or after the action, depending on if it's a before or after filter. Classes used as around filters can also use the same filter method, which will get run in the same way. The method must yield to execute the action. Alternatively, it can have both a before and an after method that are run before and after the action.

The Rails API documentation has more information on using filters.

7. Verification

-

Verifications make sure certain criterias are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the params, session or flash hashes or that a certain HTTP method was used or that the request was made using XMLHTTPRequest (Ajax). The default action taken when these criterias are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the API codumentation as "essentially a special kind of before_filter".

-

Let's see how we can use verification to make sure the user supplies a username and a password in order to log in:

+

Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the params, session or flash hashes or that a certain HTTP method was used or that the request was made using XMLHTTPRequest (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the API documentation as "essentially a special kind of before_filter".

+

Here's an example of using verification to make sure the user supplies a username and a password in order to log in:

verify :params => [:username, :password], :render => {:action => "new"}, :add_flash => {:error => "Username and password required to log in"}, - :only => :create #Only run this verification for the "create" action + :only => :create # Only run this verification for the "create" action end
-

8. The request and response objects

+

8. Request Forgery Protection

-

In every controller there are two accessor methods pointing to the request and the response objects associated with the request cycle that is currently in execution. The request method contains an instance of AbstractRequest and the response method contains the response object representing what is going to be sent back to the client.

-

8.1. The request

-

The request object contains a lot of useful information about the request coming in from the client. To get a full list of the available methods, refer to the API documentation.

+

Cross-site request forgery is a type of attack in which a site tricks a user into making requests on another site, possibly adding, modifying or deleting data on that site without the user's knowledge or permission. The first step to avoid this is to make sure all "destructive" actions (create, update and destroy) can only be accessed with non-GET requests. If you're following RESTful conventions you're already doing this. However, a malicious site can still send a non-GET request to your site quite easily, and that's where the request forgery protection comes in. As the name says, it protects from forged requests. The way this is done is to add a non-guessable token which is only known to your server to each request. This way, if a request comes in without the proper token, it will be denied access.

+

If you generate a form like this:

+
+
+
<% form_for @user do |f| -%>
+  <%= f.text_field :username %>
+  <%= f.text_field :password -%>
+<% end -%>
+
+

You will see how the token gets added as a hidden field:

+
+
+
<form action="/users/1" method="post">
+<div><!-- ... --><input type="hidden" value="67250ab105eb5ad10851c00a5621854a23af5489" name="authenticity_token"/></div>
+<!-- Fields -->
+</form>
+
+

Rails adds this token to every form that's generated using the form helpers, so most of the time you don't have to worry about it. If you're writing a form manually or need to add the token for another reason, it's available through the method form_authenticity_token:

+
+
Example: Add a JavaScript variable containing the token for use with Ajax
+
+
<%= javascript_tag "MyApp.authenticity_token = '#{form_authenticity_token}'" %>
+
+

The Security Guide has more about this and a lot of other security-related issues that you should be aware of when developing a web application.

+
+

9. The request and response Objects

+
+

In every controller there are two accessor methods pointing to the request and the response objects associated with the request cycle that is currently in execution. The request method contains an instance of AbstractRequest and the response method returns a response object representing what is going to be sent back to the client.

+

9.1. The request Object

+

The request object contains a lot of useful information about the request coming in from the client. To get a full list of the available methods, refer to the API documentation. Among the properties that you can access on this object:

  • @@ -812,7 +925,7 @@ host - The hostname used for this request.

  • -domain - The hostname without the first part (usually "www"). +domain - The hostname without the first segment (usually "www").

  • @@ -861,11 +974,10 @@ url - The entire URL used for the request.

-

8.1.1. path_parameters, query_parameters and request_parameters

-

TODO: Does this belong here?

-

Rails collects all of the parameters sent along with the request in the params hash, whether they are sent as part of the query string or the post body. The request object has three accessors that give you access to these parameters depending on where they came from. The query_parameters hash contains parameters that were sent as part of the query string while the request_parameters hash contains parameters sent as part of the post body. The path_parameters hash contains parameters that were recognised by the routing as being part of the path leading to this particular controller and action.

-

8.2. The response

-

The response objects is not usually used directly, but is built up during the execution of the action and rendering of the data that is being sent back to the user, but sometimes - like in an after filter - it can be useful to access the response directly. Some of these accessor methods also have setters, allowing you to change their values.

+

9.1.1. path_parameters, query_parameters and request_parameters

+

Rails collects all of the parameters sent along with the request in the params hash, whether they are sent as part of the query string or the post body. The request object has three accessors that give you access to these parameters depending on where they came from. The query_parameters hash contains parameters that were sent as part of the query string while the request_parameters hash contains parameters sent as part of the post body. The path_parameters hash contains parameters that were recognized by the routing as being part of the path leading to this particular controller and action.

+

9.2. The response Object

+

The response object is not usually used directly, but is built up during the execution of the action and rendering of the data that is being sent back to the user, but sometimes - like in an after filter - it can be useful to access the response directly. Some of these accessor methods also have setters, allowing you to change their values.

  • @@ -892,11 +1004,25 @@ content_type - The content type of the response. charset - The character set being used for the response. Default is "utf8".

  • +
  • +

    +headers - Headers used for the response. +

    +
+

9.2.1. Setting Custom Headers

+

If you want to set custom headers for a response then response.headers is the place to do it. The headers attribute is a hash which maps header names to their values, and Rails will set some of them - like "Content-Type" - automatically. If you want to add or change a header, just assign it to headers with the name and value:

+
+
+
response.headers["Content-Type"] = "application/pdf"
+
-

9. HTTP Basic Authentication

+

10. HTTP Basic Authentication

-

Rails comes with built-in HTTP Basic authentication. This is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, we will create an administration section which will only be available by entering a username and a password into the browser's HTTP Basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, authenticate_or_request_with_http_basic.

+

Rails comes with built-in HTTP Basic authentication. This is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, consider an administration section which will only be available by entering a username and a password into the browser's HTTP Basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, authenticate_or_request_with_http_basic.

- +
Warning Be careful when using (or just don't use) "outside" data (params, cookies, etc) to locate the file on disk, as this is a security risk as someone could gain access to files they are not meant to have access to.Be careful when using (or just don't use) "outside" data (params, cookies, etc) to locate the file on disk, as this is a security risk that might allow someone to gain access to files they are not meant to see.
@@ -986,8 +1112,8 @@ http://www.gnu.org/software/src-highlite --> It is not recommended that you stream static files through Rails if you can instead keep them in a public folder on your web server. It is much more efficient to let the user download the file directly using Apache or another web server, keeping the request from unnecessarily going through the whole Rails stack.
-

10.2. RESTful downloads

-

While send_data works just fine, if you are creating a RESTful application having separate actions for file downloads is usually not necessary. In REST terminology, the PDF file from the example above can be considered just another representation of the client resource. Rails provides an easy and quite sleek way of doing "RESTful downloads". Let's try to rewrite the example so that the PDF download is a part of the show action:

+

11.2. RESTful Downloads

+

While send_data works just fine, if you are creating a RESTful application having separate actions for file downloads is usually not necessary. In REST terminology, the PDF file from the example above can be considered just another representation of the client resource. Rails provides an easy and quite sleek way of doing "RESTful downloads". Here's how you can rewrite the example so that the PDF download is a part of the show action, without any streaming:

end
-

In order for this example to work, we have to add the PDF MIME type to Rails. This can be done by adding the following line to the file config/initializers/mime_types.rb:

+

In order for this example to work, you have to add the PDF MIME type to Rails. This can be done by adding the following line to the file config/initializers/mime_types.rb:

GET /clients/1.pdf
-

11. Parameter filtering

+

12. Parameter Filtering

-

Rails keeps a log file for each environment (development, test and production) in the "log" folder. These are extremely useful when debugging what's actually going on in your application, but in a live application you may not want every bit of information to be stored in the log file. The filter_parameter_logging method can be used to filter out sensitive information from the log. It works by replacing certain keys in the params hash with "[FILTERED]" as they are written to the log. As an example, let's see how to filter all parameters with keys that include "password":

+

Rails keeps a log file for each environment (development, test and production) in the "log" folder. These are extremely useful when debugging what's actually going on in your application, but in a live application you may not want every bit of information to be stored in the log file. The filter_parameter_logging method can be used to filter out sensitive information from the log. It works by replacing certain values in the params hash with "[FILTERED]" as they are written to the log. As an example, let's see how to filter all parameters with keys that include "password":

The method works recursively through all levels of the params hash and takes an optional second parameter which is used as the replacement string if present. It can also take a block which receives each key in return and replaces those for which the block returns true.

-

12. Rescue

+

13. Rescue

Most likely your application is going to contain bugs or otherwise throw an exception that needs to be handled. For example, if the user follows a link to a resource that no longer exists in the database, Active Record will throw the ActiveRecord::RecordNotFound exception. Rails' default exception handling displays a 500 Server Error message for all exceptions. If the request was made locally, a nice traceback and some added information gets displayed so you can figure out what went wrong and deal with it. If the request was remote Rails will just display a simple "500 Server Error" message to the user, or a "404 Not Found" if there was a routing error or a record could not be found. Sometimes you might want to customize how these errors are caught and how they're displayed to the user. There are several levels of exception handling available in a Rails application:

-

12.1. The default 500 and 404 templates

+

13.1. The Default 500 and 404 Templates

By default a production application will render either a 404 or a 500 error message. These messages are contained in static HTML files in the public folder, in 404.html and 500.html respectively. You can customize these files to add some extra information and layout, but remember that they are static; i.e. you can't use RHTML or layouts in them, just plain HTML.

-

12.2. rescue_from

-

If you want to do something a bit more elaborate when catching errors, you can use rescue_from, which handles exceptions of a certain type (or multiple types) in an entire controller and its subclasses. When an exception occurs which is caught by a rescue_from directive, the exception object is passed to the handler. The handler can be a method or a Proc object passed to the :with option. You can also use a block directly instead of an explicit Proc object.

-

Let's see how we can use rescue_from to intercept all ActiveRecord::RecordNotFound errors and do something with them.

+

13.2. rescue_from

+

If you want to do something a bit more elaborate when catching errors, you can use rescue_from, which handles exceptions of a certain type (or multiple types) in an entire controller and its subclasses. When an exception occurs which is caught by a rescue_from directive, the exception object is passed to the handler. The handler can be a method or a Proc object passed to the :with option. You can also use a block directly instead of an explicit Proc object.

+

Here's how you can use rescue_from to intercept all ActiveRecord::RecordNotFound errors and do something with them.

+ + + + + + + + +
+ + + +
+

Active Record Validations and Callbacks

+
+
+

This guide teaches you how to work with the lifecycle of your Active Record objects. More precisely, you will learn how to validate the state of your objects before they go into the database and also how to teach them to perform custom operations at certain points of their lifecycles.

+

After reading this guide and trying out the presented concepts, we hope that you'll be able to:

+
    +
  • +

    +Correctly use all the built-in Active Record validation helpers +

    +
  • +
  • +

    +Create your own custom validation methods +

    +
  • +
  • +

    +Work with the error messages generated by the validation proccess +

    +
  • +
  • +

    +Register callback methods that will execute custom operations during your objects lifecycle, for example before/after they are saved. +

    +
  • +
  • +

    +Create special classes that encapsulate common behaviour for your callbacks +

    +
  • +
  • +

    +Create Observers - classes with callback methods specific for each of your models, keeping the callback code outside your models' declarations. +

    +
  • +
+
+
+

1. Active Record Validations

+
+
+

2. Credits

+
+
+

3. Changelog

+ + +
+
+ + diff --git a/railties/doc/guides/html/authors.html b/railties/doc/guides/html/authors.html index 973cf7cd2e..a54135b14d 100644 --- a/railties/doc/guides/html/authors.html +++ b/railties/doc/guides/html/authors.html @@ -218,6 +218,19 @@ work with Rails. His near-daily links and other blogging can be found at Eventioz. He has been using Rails since 2006 and contributing since early 2008. Can be found at gmail, twitter, freenode, everywhere as miloops.

+
+
+
+
diff --git a/railties/doc/guides/html/debugging_rails_applications.html b/railties/doc/guides/html/debugging_rails_applications.html index bf1e442d59..95f5b39e4c 100644 --- a/railties/doc/guides/html/debugging_rails_applications.html +++ b/railties/doc/guides/html/debugging_rails_applications.html @@ -208,6 +208,8 @@ ul#navMain {
  • inspect
  • +
  • Debugging Javascript
  • +
  • @@ -253,6 +255,19 @@ ul#navMain {
  • + Debugging Memory Leaks + +
  • +
  • + Plugins for Debugging +
  • +
  • References
  • @@ -325,10 +340,7 @@ http://www.gnu.org/software/src-highlite -->

    You'll see something like this:

    -
    +
    --- !ruby/object:Post
     attributes:
       updated_at: 2008-09-05 22:55:47
    @@ -340,8 +352,8 @@ attributes:
     attributes_cache: {}
     
     
    -Title: Rails debugging guide
    -
    +Title: Rails debugging guide +

    1.2. to_yaml

    Displaying an instance variable, or any other object or method, in yaml format can be achieved this way:

    @@ -358,10 +370,7 @@ http://www.gnu.org/software/src-highlite -->

    The to_yaml method converts the method to YAML format leaving it more readable, and then the simple_format helper is used to render each line as in the console. This is how debug method does its magic.

    As a result of this, you will have something like this in your view:

    -
    +
    --- !ruby/object:Post
     attributes:
     updated_at: 2008-09-05 22:55:47
    @@ -372,8 +381,8 @@ id: "1"
     created_at: 2008-09-05 22:55:47
     attributes_cache: {}
     
    -Title: Rails debugging guide
    -
    +Title: Rails debugging guide +

    1.3. inspect

    Another useful method for displaying object values is inspect, especially when working with arrays or hashes. This will print the object value as a string. For example:

    @@ -389,14 +398,37 @@ http://www.gnu.org/software/src-highlite -->

    Will be rendered as follows:

    +
    +
    [1, 2, 3, 4, 5]
    +
    +Title: Rails debugging guide
    +
    +

    1.4. Debugging Javascript

    +

    Rails has built-in support to debug RJS, to active it, set ActionView::Base.debug_rjs to true, this will specify whether RJS responses should be wrapped in a try/catch block that alert()s the caught exception (and then re-raises it).

    +

    To enable it, add the following in the Rails::Initializer do |config| block inside environment.rb:

    +
    -
    [1, 2, 3, 4, 5]
    -
    -Title: Rails debugging guide
    +
    config.action_view[:debug_rjs] = true
    +
    +

    Or, at any time, setting ActionView::Base.debug_rjs to true:

    +
    +
    +
    ActionView::Base.debug_rjs = true
     
    +
    + + + +
    +Tip +For more information on debugging javascript refer to Firebug, the popular debugger for Firefox.
    +

    2. The Logger

    @@ -488,11 +520,8 @@ http://www.gnu.org/software/src-highlite -->

    Here's an example of the log generated by this method:

    -
    -
    Processing PostsController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST]
    +
    +
    Processing PostsController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST]
       Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGl
     vbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b06c4d724596a4
       Parameters: {"commit"=>"Create", "post"=>{"title"=>"Debugging Rails",
    @@ -506,8 +535,8 @@ Post should be valid: true
      'I''m learning how to print in logs!!!', 'f', '2008-09-08 14:52:54')
     The post was saved and now is the user is going to be redirected...
     Redirected to #<Post:0x20af760>
    -Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/posts]
    -
    +Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/posts] +

    Adding extra logging like this makes it easy to search for unexpected or unusual behavior in your logs. If you add extra logging, be sure to make sensible use of log levels, to avoid filling your production logs with useless trivia.

    3. Debugging with ruby-debug

    @@ -540,12 +569,9 @@ http://www.gnu.org/software/src-highlite -->

    If you see the message in the console or logs:

    -
    -
    ***** Debugger requested, but was not available: Start server with --debugger to enable *****
    -
    +
    +
    ***** Debugger requested, but was not available: Start server with --debugger to enable *****
    +

    Make sure you have started your web server with the option —debugger:

    @posts = Post.find(:all)
     (rdb:7)
    -

    Now it's time to play and dig into your application. A good place to start is by asking the debugger for help… so type: help (You didn't see that coming, right?)

    +

    Now it's time to explore and dig into your application. A good place to start is by asking the debugger for help… so type: help (You didn't see that coming, right?)

    (rdb:7) help
    @@ -969,16 +995,104 @@ No breakpoints.

    Here's a good start for an .rdebugrc:

    +
    +
    set autolist
    +set forcestep
    +set listsize 25
    +
    +
    +

    4. Debugging Memory Leaks

    +
    +

    A Ruby application (on Rails or not), can leak memory - either in the Ruby code or at the C code level.

    +

    In this section, you will learn how to find and fix such leaks by using Bleak House and Valgrind debugging tools.

    +

    4.1. BleakHouse

    +

    BleakHouse is a library for finding memory leaks.

    +

    If a Ruby object does not go out of scope, the Ruby Garbage Collector won't sweep it since it is referenced somewhere. Leaks like this can grow slowly and your application will consume more and more memory, gradually affecting the overall system performance. This tool will help you find leaks on the Ruby heap.

    +

    To install it run:

    +
    +
    +
    sudo gem install bleak_house
    +
    +

    Then setup you application for profiling. Then add the following at the bottom of config/environment.rb:

    +
    -
    set autolist
    -set forcestep
    -set listsize 25
    +
    require 'bleak_house' if ENV['BLEAK_HOUSE']
     
    +

    Start a server instance with BleakHouse integration:

    +
    +
    +
    RAILS_ENV=production BLEAK_HOUSE=1 ruby-bleak-house ./script/server
    +
    +

    Make sure to run a couple hundred requests to get better data samples, then press CTRL-C. The server will stop and Bleak House will produce a dumpfile in /tmp:

    +
    +
    +
    ** BleakHouse: working...
    +** BleakHouse: complete
    +** Bleakhouse: run 'bleak /tmp/bleak.5979.0.dump' to analyze.
    +
    +

    To analyze it, just run the listed command. The top 20 leakiest lines will be listed:

    +
    +
    +
      191691 total objects
    +  Final heap size 191691 filled, 220961 free
    +  Displaying top 20 most common line/class pairs
    +  89513 __null__:__null__:__node__
    +  41438 __null__:__null__:String
    +  2348 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:Array
    +  1508 /opt/local//lib/ruby/gems/1.8/specifications/gettext-1.90.0.gemspec:14:String
    +  1021 /opt/local//lib/ruby/gems/1.8/specifications/heel-0.2.0.gemspec:14:String
    +   951 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:111:String
    +   935 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:String
    +   834 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:146:Array
    +  ...
    +
    +

    This way you can find where your application is leaking memory and fix it.

    +

    If BleakHouse doesn't report any heap growth but you still have memory growth, you might have a broken C extension, or real leak in the interpreter. In that case, try using Valgrind to investigate further.

    +

    4.2. Valgrind

    +

    Valgrind is a Linux-only application for detecting C-based memory leaks and race conditions.

    +

    There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in detail. For example, a C extension in the interpreter calls malloc() but is doesn't properly call free(), this memory won't be available until the app terminates.

    +

    For further information on how to install Valgrind and use with Ruby, refer to Valgrind and Ruby by Evan Weaver.

    -

    4. References

    +

    5. Plugins for Debugging

    +
    +

    There are some Rails plugins to help you to find errors and debug your application. Here is a list of useful plugins for debugging:

    +
      +
    • +

      +Footnotes: Every Rails page has footnotes that link give request information and link back to your source via TextMate. +

      +
    • +
    • +

      +Query Trace: Adds query origin tracing to your logs. +

      +
    • +
    • +

      +Query Stats: A Rails plugin to track database queries. +

      +
    • +
    • +

      +Query Reviewer: This rails plugin not only runs "EXPLAIN" before each of your select queries in development, but provides a small DIV in the rendered output of each page with the summary of warnings for each query that it analyzed. +

      +
    • +
    • +

      +Exception Notifier: Provides a mailer object and a default set of templates for sending email notifications when errors occur in a Rails application. +

      +
    • +
    • +

      +Exception Logger: Logs your Rails exceptions in the database and provides a funky web interface to manage them. +

      +
    • +
    +
    +

    6. References

    -

    5. Changelog

    +

    7. Changelog

    • +November 3, 2008: Accepted for publication. Added RJS, memory leaks and plugins chapters by Emilio Tagua +

      +
    • +
    • +

      October 19, 2008: Copy editing pass by Mike Gunderloy

    • diff --git a/railties/doc/guides/html/finders.html b/railties/doc/guides/html/finders.html index f8396bb517..18bc32306f 100644 --- a/railties/doc/guides/html/finders.html +++ b/railties/doc/guides/html/finders.html @@ -373,27 +373,21 @@ http://www.gnu.org/software/src-highlite -->
      SELECT * FROM +clients+ WHERE (+clients+.+id+ IN (1,2))
       
    -
    +
    >> Client.find(1,2)
     => [#<Client id: 1, name: => "Ryan", locked: false, orders_count: 2,
       created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50">,
       #<Client id: 2, name: => "Michael", locked: false, orders_count: 3,
    -  created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">]
    -
    + created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">]
    +

    Note that if you pass in a list of numbers that the result will be returned as an array, not as a single Client object.

    If you wanted to find the first client you would simply type Client.first and that would find the first client created in your clients table:

    -
    +
    >> Client.first
     => #<Client id: 1, name: => "Ryan", locked: false, orders_count: 2,
    -  created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50">
    -
    + created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50"> +

    If you were running script/server you might see the following output:

    Indicating the query that Rails has performed on your database.

    To find the last client you would simply type Client.find(:last) and that would find the last client created in your clients table:

    -
    +
    >> Client.find(:last)
     => #<Client id: 2, name: => "Michael", locked: false, orders_count: 3,
    -  created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">
    -
    + created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40"> +

    To find all the clients you would simply type Client.all and that would find all the clients in your clients table:

    -
    +
    >> Client.all
     => [#<Client id: 1, name: => "Ryan", locked: false, orders_count: 2,
       created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50">,
       #<Client id: 2, name: => "Michael", locked: false, orders_count: 3,
    -  created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">]
    -
    + created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">] +

    As alternatives to calling Client.first, Client.last, and Client.all, you can use the class methods Client.first, Client.last, and Client.all instead. Client.first, Client.last and Client.all just call their longer counterparts: Client.find(:first), Client.find(:last) and Client.find(:all) respectively.

    Be aware that Client.first/Client.find(:first) and Client.last/Client.find(:last) will both return a single object, where as Client.all/Client.find(:all) will return an array of Client objects, just as passing in an array of ids to find will do also.

    @@ -510,12 +498,9 @@ http://www.gnu.org/software/src-highlite -->

    This could possibly cause your database server to raise an unexpected error, for example MySQL will throw back this error:

    -
    -
    Got a packet bigger than 'max_allowed_packet' bytes: _query_
    -
    +
    +
    Got a packet bigger than 'max_allowed_packet' bytes: _query_
    +

    Where query is the actual query used to get that error.

    In this example it would be better to use greater-than and less-than operators in SQL, like so:

    diff --git a/railties/doc/guides/html/form_helpers.html b/railties/doc/guides/html/form_helpers.html index 28c317411b..7ff4a13a6a 100644 --- a/railties/doc/guides/html/form_helpers.html +++ b/railties/doc/guides/html/form_helpers.html @@ -220,6 +220,16 @@ ul#navMain {
  • +
  • + Making select boxes with ease + +
  • @@ -227,7 +237,7 @@ ul#navMain {

    Rails form helpers

    -

    Forms in web applications are an essential interface for user input. They are also often considered the most complex elements of HTML. Rails deals away with these complexities by providing numerous view helpers for generating form markup. However, since they have different use-cases, developers are required to know all the differences between similar helper methods before putting them to use.

    +

    Forms in web applications are an essential interface for user input. However, form markup can quickly become tedious to write and maintain because of form control naming and their numerous attributes. Rails deals away with these complexities by providing view helpers for generating form markup. However, since they have different use-cases, developers are required to know all the differences between similar helper methods before putting them to use.

    In this guide we will:

    • @@ -392,7 +402,7 @@ a submit element. Warning -Do not delimit the second hash without doing so with the first hash, otherwise your method invocation will result in an ugly expecting tASSOC syntax error. +Do not delimit the second hash without doing so with the first hash, otherwise your method invocation will result in an expecting tASSOC syntax error.

    1.3. Checkboxes, radio buttons and other controls

    @@ -431,7 +441,7 @@ output: Important -Always use labels for each checkbox and radio button. They associate text with a specific option, while also providing a larger clickable region. +Always use labels for each checkbox and radio button. They associate text with a specific option and provide a larger clickable region.

    Other form controls we might mention are the text area, password input and hidden input:

    @@ -458,7 +468,7 @@ output:

    1.4. How do forms with PUT or DELETE methods work?

    Rails framework encourages RESTful design of your applications, which means you'll be making a lot of "PUT" and "DELETE" requests (besides "GET" and "POST"). Still, most browsers don't support methods other than "GET" and "POST" when it comes to submitting forms. How does this work, then?

    -

    Rails works around this issue by emulating other methods over POST with a hidden input named "method" that is set to reflect the _real method:

    +

    Rails works around this issue by emulating other methods over POST with a hidden input named "_method" that is set to reflect the wanted method:

    form_tag(search_path, :method => "put")
    @@ -562,6 +572,64 @@ form_for(@article)
    When you're using STI (single-table inheritance) with your models, you can't rely on record identification on a subclass if only their parent class is declared a resource. You will have to specify the model name, :url and :method explicitly.
    +
    +

    3. Making select boxes with ease

    +
    +

    Select boxes in HTML require a significant amount of markup (one OPTION element for each option to choose from), therefore it makes the most sense for them to be dynamically generated from data stored in arrays or hashes.

    +

    Here is what our wanted markup might look like:

    +
    +
    +
    <select name="city_id" id="city_id">
    +  <option value="1">Lisabon</option>
    +  <option value="2">Madrid</option>
    +  ...
    +  <option value="12">Berlin</option>
    +</select>
    +
    +

    Here we have a list of cities where their names are presented to the user, but internally we want to handle just their IDs so we keep them in value attributes. Let's see how Rails can help out here.

    +

    3.1. The select tag and options

    +

    The most generic helper is select_tag, which — as the name implies — simply generates the SELECT tag that encapsulates the options:

    +
    +
    +
    <%= select_tag(:city_id, '<option value="1">Lisabon</option>...') %>
    +
    +

    This is a start, but it doesn't dynamically create our option tags. We had to pass them in as a string.

    +

    We can generate option tags with the options_for_select helper:

    +
    +
    +
    <%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...]) %>
    +
    +output:
    +
    +<option value="1">Lisabon</option>
    +<option value="2">Madrid</option>
    +...
    +
    +

    For input data we used a nested array where each element has two elements: visible value (name) and internal value (ID).

    +

    Now you can combine select_tag and options_for_select to achieve the desired, complete markup:

    +
    +
    +
    <%= select_tag(:city_id, options_for_select(...)) %>
    +
    +

    Sometimes, depending on our application's needs, we also wish a specific option to be pre-selected. The options_for_select helper supports this with an optional second argument:

    +
    +
    +
    <%= options_for_select(cities_array, 2) %>
    +
    +output:
    +
    +<option value="1">Lisabon</option>
    +<option value="2" selected="selected">Madrid</option>
    +...
    +
    +

    So whenever Rails sees that the internal value of an option being generated matches this value, it will add the selected attribute to that option.

    +

    3.2. Select boxes for dealing with models

    +

    Until now we've covered how to make generic select boxes, but in most cases our form controls will be tied to a specific database model. So, to continue from our previous examples, let's assume that we have a "Person" model with a city_id attribute.

    +
    +
    +
    ...
    +
    +

    diff --git a/railties/doc/guides/html/getting_started_with_rails.html b/railties/doc/guides/html/getting_started_with_rails.html index 797ae2fb3a..1b2eac0ce5 100644 --- a/railties/doc/guides/html/getting_started_with_rails.html +++ b/railties/doc/guides/html/getting_started_with_rails.html @@ -1457,51 +1457,57 @@ http://www.gnu.org/software/src-highlite -->

    At this point, it’s worth looking at some of the tools that Rails provides to eliminate duplication in your code. In particular, you can use partials to clean up duplication in views and filters to help with duplication in controllers.

    7.1. Using Partials to Eliminate View Duplication

    As you saw earlier, the scaffold-generated views for the new and edit actions are largely identical. You can pull the shared code out into a partial template. This requires editing the new and edit views, and adding a new template:

    -

    new.html.erb: -[source, ruby]

    +

    new.html.erb:

    -
    -
    <h1>New post</h1>
    +
    +
    <h1>New post</h1>
     
    -<%= render :partial => "form" %>
    +<%= render :partial => "form" %>
     
    -<%= link_to 'Back', posts_path %>
    -
    -

    edit.html.erb: -[source, ruby]

    +<%= link_to 'Back', posts_path %> +
    +

    edit.html.erb:

    -
    -
    <h1>Editing post</h1>
    +
    +
    <h1>Editing post</h1>
     
    -<%= render :partial => "form" %>
    +<%= render :partial => "form" %>
     
    -<%= link_to 'Show', @post %> |
    -<%= link_to 'Back', posts_path %>
    -
    -

    _form.html.erb: -[source, ruby]

    +<%= link_to 'Show', @post %> | +<%= link_to 'Back', posts_path %> +
    +

    _form.html.erb:

    -
    -
    <% form_for(@post) do |f| %>
    -  <%= f.error_messages %>
    -
    -  <p>
    -    <%= f.label :name %><br />
    -    <%= f.text_field :name %>
    -  </p>
    -  <p>
    -    <%= f.label :title, "title" %><br />
    -    <%= f.text_field :title %>
    -  </p>
    -  <p>
    -    <%= f.label :content %><br />
    -    <%= f.text_area :content %>
    -  </p>
    -  <p>
    -    <%= f.submit "Save" %>
    -  </p>
    -<% end %>
    -
    +
    +
    <% form_for(@post) do |f| %>
    +  <%= f.error_messages %>
    +
    +  <p>
    +    <%= f.label :name %><br />
    +    <%= f.text_field :name %>
    +  </p>
    +  <p>
    +    <%= f.label :title, "title" %><br />
    +    <%= f.text_field :title %>
    +  </p>
    +  <p>
    +    <%= f.label :content %><br />
    +    <%= f.text_area :content %>
    +  </p>
    +  <p>
    +    <%= f.submit "Save" %>
    +  </p>
    +<% end %>
    +

    Now, when Rails renders the new or edit view, it will insert the _form partial at the indicated point. Note the naming convention for partials: if you refer to a partial named form inside of a view, the corresponding file is _form.html.erb, with a leading underscore.

    For more information on partials, refer to the Layouts and Rending in Rails guide.

    7.2. Using Filters to Eliminate Controller Duplication

    @@ -1721,32 +1727,32 @@ http://www.gnu.org/software/src-highlite -->
  • -+app/helpers/comments_helper.rb - A view helper file +app/helpers/comments_helper.rb - A view helper file

  • -+app/views/comments/index.html.erb - The view for the index action +app/views/comments/index.html.erb - The view for the index action

  • -+app/views/comments/show.html.erb - The view for the show action +app/views/comments/show.html.erb - The view for the show action

  • -+app/views/comments/new.html.erb - The view for the new action +app/views/comments/new.html.erb - The view for the new action

  • -+app/views/comments/edit.html.erb - The view for the edit action +app/views/comments/edit.html.erb - The view for the edit action

  • -+test/functional/comments_controller_test.rb - The functional tests for the controller +test/functional/comments_controller_test.rb - The functional tests for the controller

  • @@ -1984,7 +1990,7 @@ http://www.gnu.org/software/src-highlite -->
    +

    Rails also comes with built-in help that you can generate using the rake command-line utility:

    +

    10. Changelog

    @@ -2010,6 +2029,11 @@ The Rails wiki
    • +November 3, 2008: Formatting patch from Dave Rothlisberger +

      +
    • +
    • +

      November 1, 2008: First approved version by Mike Gunderloy

    • diff --git a/railties/doc/guides/html/index.html b/railties/doc/guides/html/index.html index 306257678b..991b10c7e8 100644 --- a/railties/doc/guides/html/index.html +++ b/railties/doc/guides/html/index.html @@ -250,7 +250,7 @@ ul#navMain {
    @@ -276,14 +276,6 @@ understand how to use routing in your own Rails applications, start here.

    @@ -318,20 +310,12 @@ Enjoy.

    -

    will run the down method fron the last 3 migrations.

    +

    will run the down method from the last 3 migrations.

    The db:migrate:redo task is a shortcut for doing a rollback and then migrating back up again. As with the db:rollback task you can use the STEP parameter if you need to go more than one version back, for example

    diff --git a/railties/doc/guides/html/testing_rails_applications.html b/railties/doc/guides/html/testing_rails_applications.html index 73634ef328..666d1dff85 100644 --- a/railties/doc/guides/html/testing_rails_applications.html +++ b/railties/doc/guides/html/testing_rails_applications.html @@ -233,7 +233,7 @@ ul#navMain {
  • What to include in your Functional Tests
  • -
  • Available Request Types for Functional Tests===
  • +
  • Available Request Types for Functional Tests
  • The 4 Hashes of the Apocalypse
  • @@ -398,15 +398,12 @@ steve:

    Fixtures can also be described using the all-too-familiar comma-separated value (CSV) file format. These files, just like YAML fixtures, are placed in the test/fixtures directory, but these end with the .csv file extension (as in celebrity_holiday_figures.csv).

    A CSV fixture looks like this:

    -
    +
    id, username, password, stretchable, comments
     1, sclaus, ihatekids, false, I like to say ""Ho! Ho! Ho!""
     2, ebunny, ihateeggs, true, Hoppity hop y'all
    -3, tfairy, ilovecavities, true, "Pull your teeth, I will"
    -
    +3, tfairy, ilovecavities, true, "Pull your teeth, I will" +

    The first line is the header. It is a comma-separated list of fields. The rest of the file is the payload: 1 record per line. A few notes about this format:

    • @@ -535,17 +532,14 @@ email(david.

      In Rails, unit tests are what you write to test your models.

    When you create a model using script/generate, among other things it creates a test stub in the test/unit folder, as well as a fixture for the model:

    -
    +
    $ script/generate model Post
     ...
     create  app/models/post.rb
     create  test/unit/post_test.rb
     create  test/fixtures/posts.yml
    -...
    -
    +... +

    The default test stub in test/unit/post_test.rb looks like this:

    +
    $ ruby unit/post_test.rb -n test_truth
     
     Loaded suite unit/post_test
    @@ -648,8 +639,8 @@ Started
     .
     Finished in 0.023513 seconds.
     
    -1 tests, 1 assertions, 0 failures, 0 errors
    -
    +1 tests, 1 assertions, 0 failures, 0 errors +

    The . (dot) above indicates a passing test. When a test fails you see an F; when a test throws an error you see an E in its place. The last line of the output is the summary.

    To see how a test failure is reported, you can add a failing test to the post_test.rb test case:

    @@ -664,10 +655,7 @@ http://www.gnu.org/software/src-highlite -->

    If you haven't added any data to the test fixture for posts, this test will fail. You can see this by running it:

    -
    +
    $ ruby unit/post_test.rb
     Loaded suite unit/post_test
     Started
    @@ -681,8 +669,8 @@ test_should_have_atleast_one_post(PostTest)
          /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run']:
     <nil> expected to not be nil.
     
    -2 tests, 2 assertions, 1 failures, 0 errors
    -
    +2 tests, 2 assertions, 1 failures, 0 errors +

    In the output, F denotes a failure. You can see the corresponding trace shown under 1) along with the name of the failing test. The next few lines contain the stack trace followed by a message which mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable every assertion provides an optional message parameter, as shown here:

    Running this test shows the friendlier assertion message:

    -
    +
    $ ruby unit/post_test.rb
     Loaded suite unit/post_test
     Started
    @@ -714,8 +699,8 @@ test_should_have_atleast_one_post(PostTest)
     Should not be nil as Posts table should have atleast one post.
     <nil> expected to not be nil.
     
    -2 tests, 2 assertions, 1 failures, 0 errors
    -
    +2 tests, 2 assertions, 1 failures, 0 errors +

    To see how an error gets reported, here's a test containing an error:

    Now you can see even more output in the console from running the tests:

    -
    +
    $ ruby unit/post_test.rb
     Loaded suite unit/post_test
     Started
    @@ -756,8 +738,8 @@ NameError: undefined local variable or method `some_undefined_variable' for #<
         /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
         /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run'
     
    -3 tests, 2 assertions, 1 failures, 1 errors
    -
    +3 tests, 2 assertions, 1 failures, 1 errors +

    Notice the E in the output. It denotes a test with error.

    @@ -1160,7 +1142,7 @@ http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite -->
    get(:view, {'id' => '12'}, nil, {'message' => 'booya!'})
     
    -

    4.2. Available Request Types for Functional Tests===

    +

    4.2. Available Request Types for Functional Tests

    If you're familiar with the HTTP protocol, you'll know that get is a type of request. There are 5 request types supported in Rails functional tests:

    • @@ -1638,16 +1620,13 @@ http://www.gnu.org/software/src-highlite -->

      In this test, @expected is an instance of TMail::Mail that you can use in your tests. It is defined in ActionMailer::TestCase. The test above uses @expected to construct an email, which it then asserts with email created by the custom mailer. The invite fixture is the body of the email and is used as the sample content to assert against. The helper read_fixture is used to read in the content from this file.

      Here's the content of the invite fixture:

      -
      +
      Hi friend@example.com,
       
       You have been invited.
       
      -Cheers!
      -
      +Cheers! +

    This is the right time to understand a little more about writing tests for your mailers. The line ActionMailer::Base.delivery_method = :test in config/environments/test.rb sets the delivery method to test mode so that email will not actually be delivered (useful to avoid spamming your users while testing) but instead it will be appended to an array (ActionMailer::Base.deliveries).

    However often in unit tests, mails will not actually be sent, simply constructed, as in the example above, where the precise content of the email is checked against what it should be.

    6.3. Functional Testing

    -- cgit v1.2.3