Merge with docrails. Also add a rake task to generate guides in your rails application :
rake doc:guides The rake task will generate guides inside doc/guides directory of your application. Open index.html to browse.
|
@ -5,7 +5,6 @@ activerecord/doc
|
|||
actionpack/doc
|
||||
actionmailer/doc
|
||||
activesupport/doc
|
||||
railties/doc
|
||||
activeresource/pkg
|
||||
activerecord/pkg
|
||||
actionpack/pkg
|
||||
|
@ -13,4 +12,6 @@ actionmailer/pkg
|
|||
activesupport/pkg
|
||||
railties/pkg
|
||||
railties/test/500.html
|
||||
railties/doc/guides/html/images
|
||||
railties/doc/guides/html/stylesheets
|
||||
*.rbc
|
||||
|
|
|
@ -406,15 +406,15 @@ module ActionView
|
|||
# ==== Examples
|
||||
# my_time = Time.now + 6.hours
|
||||
#
|
||||
# # Generates a select field for minutes that defaults to the minutes for the time in my_time
|
||||
# select_minute(my_time)
|
||||
# # Generates a select field for hours that defaults to the hour for the time in my_time
|
||||
# select_hour(my_time)
|
||||
#
|
||||
# # Generates a select field for minutes that defaults to the number given
|
||||
# select_minute(14)
|
||||
# # Generates a select field for hours that defaults to the number given
|
||||
# select_hour(13)
|
||||
#
|
||||
# # Generates a select field for minutes that defaults to the minutes for the time in my_time
|
||||
# # Generates a select field for hours that defaults to the minutes for the time in my_time
|
||||
# # that is named 'stride' rather than 'second'
|
||||
# select_minute(my_time, :field_name => 'stride')
|
||||
# select_hour(my_time, :field_name => 'stride')
|
||||
#
|
||||
def select_hour(datetime, options = {}, html_options = {})
|
||||
DateTimeSelector.new(datetime, options, html_options).select_hour
|
||||
|
|
|
@ -624,7 +624,8 @@ module ActiveRecord
|
|||
# Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
|
||||
# [collection.delete(object, ...)]
|
||||
# Removes one or more objects from the collection by setting their foreign keys to +NULL+.
|
||||
# This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model.
|
||||
# Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>,
|
||||
# and deleted if they're associated with <tt>:dependent => :delete_all</tt>.
|
||||
# [collection=objects]
|
||||
# Replaces the collections content by deleting and adding objects as appropriate.
|
||||
# [collection_singular_ids]
|
||||
|
|
|
@ -183,7 +183,13 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
|
||||
# Remove +records+ from this association. Does not destroy +records+.
|
||||
# Removes +records+ from this association calling +before_remove+ and
|
||||
# +after_remove+ callbacks.
|
||||
#
|
||||
# This method is abstract in the sense that +delete_records+ has to be
|
||||
# provided by descendants. Note this method does not imply the records
|
||||
# are actually removed from the database, that depends precisely on
|
||||
# +delete_records+. They are in any case removed from the collection.
|
||||
def delete(*records)
|
||||
records = flatten_deeper(records)
|
||||
records.each { |record| raise_on_type_mismatch(record) }
|
||||
|
|
|
@ -61,6 +61,7 @@ module ActiveRecord
|
|||
record.save
|
||||
end
|
||||
|
||||
# Deletes the records according to the <tt>:dependent</tt> option.
|
||||
def delete_records(records)
|
||||
case @reflection.options[:dependent]
|
||||
when :destroy
|
||||
|
|
|
@ -321,7 +321,7 @@ module ActiveSupport #:nodoc:
|
|||
# character.
|
||||
#
|
||||
# Example:
|
||||
# 'こにちわ'.mb_chars.slice(2..3).to_s #=> "ちわ"
|
||||
# 'こんにちは'.mb_chars.slice(2..3).to_s #=> "にち"
|
||||
def slice(*args)
|
||||
if args.size > 2
|
||||
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
|
||||
|
|
|
@ -272,58 +272,51 @@ Rake::RDocTask.new { |rdoc|
|
|||
rdoc.rdoc_files.include('lib/commands/**/*.rb')
|
||||
}
|
||||
|
||||
# In this array, one defines the guides for which HTML output should be
|
||||
# generated. Specify the folder names of the guides. If the .txt filename
|
||||
# doesn't equal its folder name, then specify a hash: { 'folder_name' => 'basename' }
|
||||
guides = [
|
||||
'getting_started_with_rails',
|
||||
# 'testing_rails_applications',
|
||||
'creating_plugins',
|
||||
'actioncontroller',
|
||||
'migrations',
|
||||
{ 'securing_rails_applications' => 'security' },
|
||||
{ 'routing' => 'routing_outside_in' },
|
||||
{ 'forms' =>'form_helpers' },
|
||||
{ 'activerecord' => 'association_basics' },
|
||||
{ 'activerecord' => 'finders' },
|
||||
{ 'debugging' => 'debugging_rails_applications' },
|
||||
{ 'caching' => 'caching_with_rails' },
|
||||
{ 'benchmarking_and_profiling' => 'preamble' },
|
||||
{ 'actionview' => 'layouts_and_rendering' }
|
||||
]
|
||||
desc "Generate guides for the framework"
|
||||
task :guides do
|
||||
require 'mizuho/generator'
|
||||
|
||||
guides_html_files = [] # autogenerated from the 'guides' variable.
|
||||
guides.each do |entry|
|
||||
if entry.is_a?(Hash)
|
||||
guide_folder = entry.keys.first
|
||||
guide_name = entry.values.first
|
||||
else
|
||||
guide_folder = entry
|
||||
guide_name = entry
|
||||
end
|
||||
input = "doc/guides/#{guide_folder}/#{guide_name}.txt"
|
||||
output = "doc/guides/#{guide_folder}/#{guide_name}.html"
|
||||
guides_html_files << output
|
||||
task output => Dir["doc/guides/#{guide_folder}/*.txt"] do
|
||||
ENV['MANUALSONRAILS_INDEX_URL'] = '../index.html'
|
||||
ENV.delete('MANUALSONRAILS_TOC')
|
||||
sh "mizuho", input, "--template", "manualsonrails", "--icons-dir", "../icons"
|
||||
source = "doc/guides/source/"
|
||||
html = "doc/guides/html/"
|
||||
FileUtils.rm_r(html) if File.directory?(html)
|
||||
FileUtils.mkdir(html)
|
||||
|
||||
template = File.expand_path("doc/guides/source/templates/guides.html.erb")
|
||||
icons = File.expand_path("doc/guides/source/icons")
|
||||
|
||||
ignore = ['icons', 'images', 'templates', 'stylesheets']
|
||||
ignore << 'active_record_basics.txt'
|
||||
|
||||
indexless = ['index.txt', 'authors.txt']
|
||||
|
||||
Dir.entries(source)[2..-1].each do |entry|
|
||||
next if ignore.include?(entry)
|
||||
|
||||
if File.directory?(File.join(source, entry))
|
||||
input = File.join(source, entry, 'index.txt')
|
||||
output = File.join(html, entry)
|
||||
else
|
||||
input = File.join(source, entry)
|
||||
output = File.join(html, entry).sub(/\.txt$/, '')
|
||||
end
|
||||
|
||||
begin
|
||||
puts "GENERATING => #{output}"
|
||||
ENV['MANUALSONRAILS_TOC'] = 'no' if indexless.include?(entry)
|
||||
Mizuho::Generator.new(input, output, template, false, icons).start
|
||||
rescue Mizuho::GenerationError
|
||||
STDERR.puts "*** ERROR"
|
||||
exit 2
|
||||
ensure
|
||||
ENV.delete('MANUALSONRAILS_TOC')
|
||||
end
|
||||
end
|
||||
|
||||
# Copy images and css files to html directory. These dirs are in .gitigore and shouldn't be source controlled.
|
||||
FileUtils.cp_r File.join(source, 'images'), File.join(html, 'images')
|
||||
FileUtils.cp_r File.join(source, 'stylesheets'), File.join(html, 'stylesheets')
|
||||
end
|
||||
|
||||
['index', 'authors'].each do |guide|
|
||||
task "doc/guides/#{guide}.html" => "doc/guides/#{guide}.txt" do
|
||||
ENV.delete('MANUALSONRAILS_INDEX_URL')
|
||||
ENV['MANUALSONRAILS_TOC'] = 'no'
|
||||
sh "mizuho", "doc/guides/#{guide}.txt", "--template", "manualsonrails", "--icons-dir", "icons"
|
||||
end
|
||||
end
|
||||
|
||||
desc "Generate HTML output for the guides"
|
||||
task :generate_guides => guides_html_files
|
||||
task :generate_guides => 'doc/guides/index.html'
|
||||
task :generate_guides => 'doc/guides/authors.html'
|
||||
|
||||
# Generate GEM ----------------------------------------------------------------------------
|
||||
|
||||
task :copy_gem_environment do
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
Helpers
|
||||
====================
|
||||
|
||||
Helper Basics
|
||||
------------------------
|
||||
|
||||
Helpers allow you to encapsulate rendering tasks as reusable functions. Helpers are modules, not classes, so their methods execute in the context in which they are called. They get included in a controller (typically the ApplicationController) using the helper function, like so
|
||||
|
||||
Class ApplicationController < ActionController::Base
|
||||
…
|
||||
helper :menu
|
||||
|
||||
def …
|
||||
end
|
||||
end
|
||||
|
||||
In this way, methods in the menu helper are made available to any view or partial in your application. These methods can accept parameters, for example controller instance variables (eg; records or record collections gathered by you current controller), items from the view or partial’s locals[] hash or items from the params[] hash. You may wish to pass your controller instance variables and items from the params[] hash to the locals hash before rendering (See the section on partials). Helper methods can also accept an executable block of code.
|
||||
|
||||
It is important to remember, though, that helpers are for rendering, and that they become available once a controller method has returned, while Rails is engaged in rendering the contents generated by a controller method. This means that helper methods are not available from within the methods of your controllers.
|
||||
|
||||
Helpers can accomplish a variety of tasks, from formatting a complex tag for embedding content for a browser plugin (eg; Flash), to assembling a menu of options appropriate for the current context of your application, to generating sections of forms that get assembled on-the-fly.
|
||||
|
||||
Helpers are organized around rendering tasks, so it is not necessary (nor necessarily desirable) to organize them around your application’s controllers or models. In fact, one of the benefits of helpers is that they are not connected via a rendering pipeline to specific controllers, like views and partials are. They can and should handle more generalized tasks.
|
||||
|
||||
Here is a very simple, pseudo-example:
|
||||
|
||||
module MenuHelper
|
||||
def menu(records, menu_options={})
|
||||
item_options = menu_options.merge({<some stuff>})
|
||||
items = records.collect |record| do
|
||||
menu_item(record, options)
|
||||
end
|
||||
content_tag(“ul”, items, options)
|
||||
end
|
||||
|
||||
def menu_item(record, item_options={}))
|
||||
action = item_options[:action]
|
||||
action ||= “show”
|
||||
content_tag(“li”, link_to(record.title, :action => action, item_options)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
This helper will require that records passed into it have certain fields (notably :title). The helper could be written to use this as a default, allowing the field to be overwritten by an element of item_options.
|
||||
|
||||
Look at the Rails API for examples of helpers included in Rails, eg; [`ActionView::Helpers::ActiveRecordHelper`](http://api.rubyonrails.org/classes/ActionView/Helpers/ActiveRecordHelper.html).
|
||||
|
||||
Passing Blocks to Helper Methods
|
||||
------------------------
|
||||
|
||||
We mentioned before that blocks can be passed to helper methods. This allows for an interesting wrinkle: a block passed to a helper method can cause it to render a partial, which can then be wrapped by the helper method’s output. This can make your helper method much more reusable. It doesn’t need to know anything about the internals about what it is rendering, it just contextualizes it for the page. You can also use the helper to modify the locals hash for the partial, based on some configuration information unique to the current controller. You could implement a flexible themes system in this way.
|
||||
|
||||
|
||||
Partials vs. Helpers?
|
||||
------------------------
|
||||
|
||||
In general, the choice between using a partial vs. using a helper depends on the amount of flexibility you need. If the task is more about reacting to conditions than performing actual rendering, you may likely want a helper method. If you want to be able to call it from a variety of views, again, you may want to use a helper method. You can expect to extract helper methods out of code in views and partials during refactoring.
|
||||
|
||||
|
||||
Tutorial -- Calling a Helper [UNFINISHED]
|
||||
------------------------
|
||||
|
||||
1. Create a Rails application using `rails helper_test`
|
||||
Notice the code:
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
helper :all # include all helpers, all the time
|
||||
For this tutorial, we'll keep this code, but you will likely want to exert more control over loading your helpers.
|
||||
|
||||
2. Configure a database of your choice for the app.
|
||||
|
||||
3. Inside of the `/app/helpers/` directory, create a new file called, `menu_helper.rb`. Write this in the file:
|
||||
|
||||
module MenuHelpers
|
||||
def menu(records, item_proc=nil)
|
||||
items = records.collect{ |record|
|
||||
menu_item(record, item_proc)
|
||||
}
|
||||
content_tag("ul", items)
|
||||
end
|
||||
|
||||
def menu_item(record, item_proc=nil)
|
||||
item_url = item_proc.call(record)
|
||||
item_url ||= { :action => :show }
|
||||
content_tag("li", link_to(record.name, item_url))
|
||||
end
|
||||
end
|
||||
|
||||
4. Create a scaffold for some object in your app, using `./script/generate scaffold widgets`.
|
||||
5. Create a database table for your widgets, with at least the fields `name` and `id`. Create a few widgets.
|
||||
6. Call the menu command twice from `index.html.erb`, once using the default action, and once supplying a Proc to generate urls.
|
|
@ -1,90 +0,0 @@
|
|||
A Guide to Using Partials
|
||||
===============================
|
||||
|
||||
This guide elaborates on the use and function of partials in Ruby on Rails. As your Rails application grows, your view templates can start to contain a lot of duplicate view code. To manage and reduce this complexity, you can by abstract view template code into partials. Partials are reusable snippets of eRB template code stored in separate files with an underscore ('_') prefix.
|
||||
|
||||
Partials can be located anywhere in the `app/views` directory. File extensions for partials work just like other template files, they bear an extension that denotes what kind of code they generate. For example, `_animal.html.erb` and `_animal.xml.erb` are valid filenames for partials.
|
||||
|
||||
Partials can be inserted in eRB template code by calling the `render` method with the `:partial` option. For example:
|
||||
|
||||
<%= render :partial => 'foo' %>
|
||||
|
||||
This inserts the result of evaluating the template `_foo.html.erb` into the parent template file at this location. Note that `render` assumes that the partial will be in the same directory as the calling parent template and have the same file extension. Partials can be located anywhere within the `app/views` directory. To use a partial located in a different directory then it the parent, add a '/' before it:
|
||||
|
||||
<%= render :partial => '/common/foo' %>
|
||||
|
||||
Loads the partial file from the `app/views/common/_foo.html.erb` directory.
|
||||
|
||||
Abstracting views into partials can be approached in a number of different ways, depending on the situation. Sometimes, the code that you are abstracting is a specialized view of an object or a collection of objects. Other times, you can look at partials as a reusable subroutine. We'll explore each of these approaches and when to use them as well as the syntax for calling them.
|
||||
|
||||
Partials as a View Subroutine
|
||||
-----------------------------
|
||||
|
||||
Using the `:locals` option, you can pass a hash of values which will be treated as local variables within the partial template.
|
||||
|
||||
<%= render :partial => "person", :locals => { :name => "david" } %>
|
||||
|
||||
The variable `name` contains the value `"david"` within the `_person.html.erb` template. Passing variables in this way allows you to create modular, reusable template files. Note that if you want to make local variables that are optional in some use cases, you will have to set them to a sentinel value such as `nil` when they have not been passed. So, in the example above, if the `name` variable is optional in some use cases, you must set:
|
||||
|
||||
<% name ||= nil -%>
|
||||
|
||||
So that you can later check:
|
||||
|
||||
<% if name -%>
|
||||
<p>Hello, <%= name %>!</p>
|
||||
<% end -%>
|
||||
|
||||
Otherwise, the if statement will throw an error at runtime.
|
||||
|
||||
Another thing to be aware of is that instance variables that are visible to the parent view template are visible to the partial. So you might be tempted to do this:
|
||||
|
||||
<%= render :partial => "person" %>
|
||||
|
||||
And then within the partial:
|
||||
|
||||
<% if @name -%>
|
||||
<p>Hello, <%= @name %>!</p>
|
||||
<% end -%>
|
||||
|
||||
The potential snag here is that if multiple templates start to rely on this partial, you will need to maintain an instance variable with the same name across all of these templates and controllers. This approach can quickly become brittle if overused.
|
||||
|
||||
Partials as a View of an Object
|
||||
--------------------------------
|
||||
|
||||
Another way to look at partials is to view them as mini-views of a particular object or instance variable. Use the `:object` option to pass an object assigns that object to an instance variable named after the partial itself. For example:
|
||||
|
||||
# Renders the partial, making @new_person available through
|
||||
# the local variable 'person'
|
||||
render :partial => "person", :object => @new_person
|
||||
|
||||
If the instance variable `name` in the parent template matches the name of the partial, you can use a shortcut:
|
||||
|
||||
render :partial => "person"
|
||||
|
||||
Now the value that was in `@person` in the parent template is accessible as `person` in the partial.
|
||||
|
||||
Partials as a View of a Collection
|
||||
-----------------------------------
|
||||
|
||||
Often it is the case that you need to display not just a single object, but a collection of objects. Rather than having to constantly nest the same partial within the same iterator code, Rails provides a syntactical shortcut using the `:collection` option:
|
||||
|
||||
# Renders a collection of the same partial by making each element
|
||||
# of @winners available through the local variable "person" as it
|
||||
# builds the complete response.
|
||||
render :partial => "person", :collection => @winners
|
||||
|
||||
This calls the `_person.html.erb` partial for each person in the `@winners` collection. This also creates a local variable within the partial called `partial_counter` which contains the index of the current value. So for example:
|
||||
|
||||
<%= partial_counter %>) <%= person -%>
|
||||
|
||||
Where `@winners` contains three people, produces the following output:
|
||||
|
||||
1) Bill
|
||||
2) Jeff
|
||||
3) Nick
|
||||
|
||||
One last detail, you can place an arbitrary snippet of code in between the objects using the `:spacer_template` option.
|
||||
|
||||
# Renders the same collection of partials, but also renders the
|
||||
# person_divider partial between each person partial.
|
||||
render :partial => "person", :collection => @winners, :spacer_template => "person_divider"
|
|
@ -1,56 +0,0 @@
|
|||
Active Record Basics
|
||||
====================
|
||||
|
||||
|
||||
|
||||
The ActiveRecord Pattern
|
||||
------------------------
|
||||
|
||||
Active Record (the library) conforms to the active record design pattern. The active record pattern is a design pattern often found in applications that use relational database. The name comes from by Martin Fowler's book *Patterns of Enterprise Application Architecture*, in which he describes an active record object as:
|
||||
|
||||
> An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.
|
||||
|
||||
So, an object that follows the active record pattern encapsulates both data and behavior; in other words, they are responsible for saving and loading to the database and also for any domain logic that acts on the data. The data structure of the Active Record should exactly match that of the database: one field in the class for each column in the table.
|
||||
|
||||
The Active Record class typically has methods that do the following:
|
||||
|
||||
* Construct an instances of an Active Record class from a SQL result
|
||||
* Construct a new class instance for insertion into the table
|
||||
* Get and set column values
|
||||
* Wrap business logic where appropriate
|
||||
* Update existing objects and update the related rows in the database
|
||||
|
||||
Mapping Your Database
|
||||
---------------------
|
||||
|
||||
### Plural tables, singular classes ###
|
||||
|
||||
### Schema lives in the database ###
|
||||
|
||||
Creating Records
|
||||
----------------
|
||||
|
||||
### Using save ###
|
||||
|
||||
### Using create ###
|
||||
|
||||
Retrieving Existing Rows
|
||||
------------------------
|
||||
|
||||
### Using find ###
|
||||
|
||||
### Using find_by_* ###
|
||||
|
||||
Editing and Updating Rows
|
||||
-------------------------
|
||||
|
||||
### Editing an instance
|
||||
|
||||
### Using update_all/update_attributes ###
|
||||
|
||||
Deleting Data
|
||||
-------------
|
||||
|
||||
### Destroying a record ###
|
||||
|
||||
### Deleting a record ###
|
|
@ -1,55 +0,0 @@
|
|||
== On The Road to Optimization ==
|
||||
=== Looking at the log file in regards to optimization ===
|
||||
|
||||
You actually have been gathering data for benchmarking throughout your development cycle. Your log files are not just for error detection they also contain very useful information on how speedy your action is behaving.
|
||||
|
||||
.Regular Log Output
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Processing MediaController#index (for 127.0.0.1 at 2008-07-17 21:30:21) [GET]
|
||||
|
||||
Session ID: BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo
|
||||
SGFzaHsABjoKQHVzZWR7AA==--cb57dad9c5e4704f0e1eddb3d498fef544faaf46
|
||||
|
||||
Parameters: {"action"=>"index", "controller"=>"media"}
|
||||
|
||||
[4;35;1mProduct Columns (0.003187)[0m [0mSHOW FIELDS FROM `products`[0m
|
||||
[4;36;1mProduct Load (0.000597)[0m [0;1mSELECT * FROM `products` WHERE (`products`.`name` = 'Escape Plane') LIMIT 1[0m
|
||||
|
||||
Rendering template within layouts/standard
|
||||
|
||||
Rendering media/index
|
||||
[4;35;1mTrack Load (0.001507)[0m [0mSELECT * FROM `tracks` WHERE (`tracks`.product_id = 1) [0m
|
||||
[4;36;1mTrack Columns (0.002280)[0m [0;1mSHOW FIELDS FROM `tracks`[0m
|
||||
|
||||
Rendered layouts/_header (0.00051)
|
||||
|
||||
*Completed in 0.04310 (23 reqs/sec) | Rendering: 0.00819 (19%) | DB: 0.00757 (17%) | 200 OK [http://localhost/media]*
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
What concerns us here is the last line of the action.
|
||||
|
||||
Completed in 0.04310 (23 reqs/sec) gives us the amount of requests this specific action can handle. 0.04310 is the total amount of time the process to complete and 23 reqs/sec is an estimation from this. As we will see this number is not strictly valid since is a single instance of the process. But it does give you a general feel as to how the action is performing.
|
||||
|
||||
Rendering: 0.00819 (19%) is the amount in milliseconds and the percentage of total time needed to complete the action for rendering the view
|
||||
|
||||
DB: 0.00757 (17%) is the amount in milliseconds and the percentage of total time needed to complete the action for querying the database
|
||||
|
||||
Pretty easy right. But wait 17+19 equals 36. 36%! where is the rest of the time going? The rest of the time is being spent processing the controller. It is not shown but it is easy to calculate. Usually there is where most of your time ends on well functions actions.
|
||||
|
||||
=== Why the Log File on it's Own is not Helpful ===
|
||||
|
||||
So why can't we just use this to test our rails application. Technically that could work, but would be very stressful and slow. You don't have time to view your log after every request to see if your code is running quickly. Also a request that runs 100 reqs/sec might simply be an outlier and really usually runs at 20 reqs/sec. It's simply not enough information to do everything we need it to do but it's a start.
|
||||
|
||||
But there is something else we must consider.
|
||||
|
||||
=== A Simple Question, a Complicated Answer ===
|
||||
|
||||
Is Completed in 0.04310 (23 reqs/sec) a good time. Seems like it doesn't it. 43 ms does not outrageous time for a dynamic page load. But is this a dynamic page load. Maybe it was all cached. In which case this is very slow. Or maybe I'm running on five year old equipment and this is actually blazing fast for my G3. The truth is that we can't answer the question given the data. This is part of benchmarking. We need a baseline. Through comparative analysis of all your pages in your app, and an simple dynamic page for a control we can determine how fast your pages are actually running and if any of them need to be optimized.
|
||||
|
||||
And now for something completely different a short statistic lesson.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
== Terminology ==
|
||||
|
||||
=== What We Mean by Benchmarking and Profiling ===
|
||||
|
||||
Benchmarking: If you are new to programing you probably have heard the term mostly in comparative reviews of computer and graphic card specs. If you done a bit of coding you've probably seen in mostly in terms of comparing one language to another or iterations of the same language.
|
||||
|
||||
Benchmarking in rails is more fine grained. It entails comparing and contrasting various parts and pages of an application against one another. Mostly one is looking for how long a page requires to render, but memory consumption is also an area of concern.
|
||||
|
||||
While benchmarking two different sets of problems can emerge. Either you find that a few pages are performing worse then the rest of your app unexpectedly or that your whole entire application is slower then it reasonably should be. From there you start to profile to find the problem.
|
||||
|
||||
Profiling: When a page or process is seen to be problematic due to speed or memory consumption we profile it. Meaning we measures the behavior as the page or process runs, particularly the frequency and duration of function calls. The goal of profiling is not to find bugs, but to eliminate bottlenecks and establish a baseline for future regression testing. It must be engaged in a carefully controlled process of measurement and analysis.
|
||||
|
||||
==== What does that actually mean? ====
|
||||
|
||||
You have to have a clear goal for when you benchmark and profile. It's very comparable to BDD where you are taking small steps towards a solution instead of trying to do it all in one large all encompassing step. A clearly defined set of expectations is essential for meaningful performance testing. We will talk more about this later.
|
||||
|
||||
==== Where Does this Leave Us ====
|
||||
|
||||
Numbers and data. You benchmark to compare, your profile to fix. It's all about gaining data to analyze and using that information to better your application. The most important thing you should take away at the moment that this must be done in a systematic way.
|
||||
|
||||
So the next logical question is how do we get this data.
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
Benchmarking and Profiling Rails
|
||||
================================
|
||||
Matthew Bergman <MzbPhoto@gmail.com>
|
||||
v0.6, September 2008
|
||||
|
||||
Benchmarking and Profiling is an important part of the development process that is not nearly enough talked about for beginning developers. Its hard enough learning a language and successfully writing an application. But without a firm understanding optimization, production ready apps are a near impossibility. No matter how well you code, or how much you know about a language there is always something that will trip up your application.
|
||||
|
||||
This article is my attempt to give the basic knowledge and methodology needed to optimize your application as painlessly as possible. We are are attempting this on two fronts. Both as a straight explanation and also through a real example of how benchmarking can speed up an application.
|
||||
|
||||
The main things that are covered are
|
||||
|
||||
* The basics of statistical analysis
|
||||
* Methodology behind benchmarking and profiling
|
||||
* Reading the log file for optimization
|
||||
* Performance Unit tests
|
||||
* Working with Ruby-Prof
|
||||
* HTTPREF #because you should know it
|
||||
* Overview of dedicated analysis options
|
||||
|
||||
There are a lot of areas we need to cover so lets start.
|
||||
|
||||
|
||||
include::definitions.txt[]
|
||||
|
||||
include::basics.txt[]
|
||||
|
||||
include::statistics.txt[]
|
||||
|
||||
include::edge_rails_features.txt[]
|
||||
|
||||
include::rubyprof.txt[]
|
||||
|
||||
include::digging_deeper.txt[]
|
||||
|
||||
include::gameplan.txt[]
|
||||
|
||||
include::appendix.txt[]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>About the Authors</title>
|
||||
<!--[if lt IE 8]>
|
||||
<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
|
||||
<![endif]-->
|
||||
<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<style type="text/css">
|
||||
div#container {
|
||||
max-width: 900px;
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
div#content {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
div#container.notoc {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.notoc div#content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
#content p tt {
|
||||
background: #eeeeee;
|
||||
border: solid 1px #cccccc;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#content dt tt {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-left: 3em;
|
||||
}
|
||||
|
||||
#content dt tt, #content pre tt {
|
||||
background: none;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#content .olist ol {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
#header {
|
||||
position: relative;
|
||||
max-width: 840px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#header.notoc {
|
||||
max-width: 580px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
width: 110px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
div#header h1#site_title {
|
||||
background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
|
||||
position: absolute;
|
||||
width: 392px;
|
||||
height: 55px;
|
||||
left: 145px;
|
||||
top: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#site_title span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#site_title_tagline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul#navMain {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: 97px;
|
||||
left: 145px;
|
||||
}
|
||||
|
||||
.left-floaty, .right-floaty {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.admonitionblock,
|
||||
.tableblock {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.admonitionblock .icon {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .content {
|
||||
border: solid 1px #ffda78;
|
||||
background: #fffebd;
|
||||
padding: 10px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .title {
|
||||
font-size: 140%;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.tableblock table {
|
||||
border: solid 1px #aaaaff;
|
||||
background: #f0f0ff;
|
||||
}
|
||||
|
||||
.tableblock th {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
.tableblock th,
|
||||
.tableblock td {
|
||||
padding: 3px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.sidebarblock {
|
||||
margin-top: 0.25em;
|
||||
margin: 1em;
|
||||
border: solid 1px #ccccbb;
|
||||
padding: 8px;
|
||||
background: #ffffe0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title {
|
||||
font-size: 140%;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-content > .para:last-child > p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header" class="notoc">
|
||||
<div id="logo">
|
||||
<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
|
||||
</div>
|
||||
|
||||
<h1 id="site_title"><span>Ruby on Rails</span></h1>
|
||||
<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
|
||||
|
||||
<ul id="navMain">
|
||||
<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
|
||||
<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="container" class="notoc">
|
||||
|
||||
<div id="content">
|
||||
<h1>About the Authors</h1>
|
||||
<div id="preamble">
|
||||
<div class="sectionbody">
|
||||
<div class="sidebarblock" id="fcheung">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title">Frederick Cheung</div>
|
||||
<div class="para"><p>Frederick Cheung is Chief Wizard at Texperts where he has been using Rails since 2006.
|
||||
He is based in Cambridge (UK) and when not consuming fine ales he blogs at <a href="http://www.spacevatican.org">spacevatican.org</a>.</p></div>
|
||||
</div></div>
|
||||
<div class="sidebarblock" id="mgunderloy">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title">Mike Gunderloy</div>
|
||||
<div class="para"><p>Mike Gunderloy is an independent consultant who brings 25 years of experience in a variety of languages to bear on his current
|
||||
work with Rails. His near-daily links and other blogging can be found at <a href="http://afreshcup.com">A Fresh Cup</a>.</p></div>
|
||||
</div></div>
|
||||
<div class="sidebarblock" id="miloops">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title">Emilio Tagua</div>
|
||||
<div class="para"><p>Emilio Tagua — a.k.a. miloops — is an Argentinian entrepreneur, developer, open source contributor and Rails evangelist.
|
||||
Cofounder of <a href="http://www.eventioz.com">Eventioz</a>. He has been using Rails since 2006 and contributing since early 2008.
|
||||
Can be found at gmail, twitter, freenode, everywhere as miloops.</p></div>
|
||||
</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,577 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Caching with Rails: An overview</title>
|
||||
<!--[if lt IE 8]>
|
||||
<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
|
||||
<![endif]-->
|
||||
<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<style type="text/css">
|
||||
div#container {
|
||||
max-width: 900px;
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
div#content {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
div#container.notoc {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.notoc div#content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
#content p tt {
|
||||
background: #eeeeee;
|
||||
border: solid 1px #cccccc;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#content dt tt {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-left: 3em;
|
||||
}
|
||||
|
||||
#content dt tt, #content pre tt {
|
||||
background: none;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#content .olist ol {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
#header {
|
||||
position: relative;
|
||||
max-width: 840px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#header.notoc {
|
||||
max-width: 580px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
width: 110px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
div#header h1#site_title {
|
||||
background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
|
||||
position: absolute;
|
||||
width: 392px;
|
||||
height: 55px;
|
||||
left: 145px;
|
||||
top: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#site_title span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#site_title_tagline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul#navMain {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: 97px;
|
||||
left: 145px;
|
||||
}
|
||||
|
||||
.left-floaty, .right-floaty {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.admonitionblock,
|
||||
.tableblock {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.admonitionblock .icon {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .content {
|
||||
border: solid 1px #ffda78;
|
||||
background: #fffebd;
|
||||
padding: 10px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .title {
|
||||
font-size: 140%;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.tableblock table {
|
||||
border: solid 1px #aaaaff;
|
||||
background: #f0f0ff;
|
||||
}
|
||||
|
||||
.tableblock th {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
.tableblock th,
|
||||
.tableblock td {
|
||||
padding: 3px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.sidebarblock {
|
||||
margin-top: 0.25em;
|
||||
margin: 1em;
|
||||
border: solid 1px #ccccbb;
|
||||
padding: 8px;
|
||||
background: #ffffe0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title {
|
||||
font-size: 140%;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-content > .para:last-child > p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header" >
|
||||
<div id="logo">
|
||||
<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
|
||||
</div>
|
||||
|
||||
<h1 id="site_title"><span>Ruby on Rails</span></h1>
|
||||
<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
|
||||
|
||||
<ul id="navMain">
|
||||
<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
|
||||
<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="sidebar">
|
||||
<h2>Chapters</h2>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="#_basic_caching">Basic Caching</a>
|
||||
<ul>
|
||||
|
||||
<li><a href="#_page_caching">Page Caching</a></li>
|
||||
|
||||
<li><a href="#_action_caching">Action Caching</a></li>
|
||||
|
||||
<li><a href="#_fragment_caching">Fragment Caching</a></li>
|
||||
|
||||
<li><a href="#_sweepers">Sweepers</a></li>
|
||||
|
||||
<li><a href="#_sql_caching">SQL Caching</a></li>
|
||||
|
||||
<li><a href="#_cache_stores">Cache stores</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_advanced_caching">Advanced Caching</a>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<h1>Caching with Rails: An overview</h1>
|
||||
<div id="preamble">
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>Everyone caches. This guide will teach you what you need to know about
|
||||
avoiding that expensive round-trip to your database and returning what you
|
||||
need to return to those hungry web clients in the shortest time possible.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 id="_basic_caching">1. Basic Caching</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>This is an introduction to the three types of caching techniques that Rails
|
||||
provides by default without the use of any third party plugins.</p></div>
|
||||
<div class="para"><p>To get started make sure Base.perform_caching is set to true for your
|
||||
environment.</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Base<span style="color: #990000">.</span>perform_caching <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
|
||||
</tt></pre></div></div>
|
||||
<h3 id="_page_caching">1.1. Page Caching</h3>
|
||||
<div class="para"><p>Page caching is a Rails mechanism which allows the request for a generated
|
||||
page to be fulfilled by the webserver, without ever having to go through the
|
||||
Rails stack at all. Obviously, this is super fast. Unfortunately, it can't be
|
||||
applied to every situation (such as pages that need authentication) and since
|
||||
the webserver is literally just serving a file from the filesystem, cache
|
||||
expiration is an issue that needs to be dealt with.</p></div>
|
||||
<div class="para"><p>So, how do you enable this super-fast cache behavior? Simple, let's say you
|
||||
have a controller called ProductController and a <em>list</em> action that lists all
|
||||
the products</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductController <span style="color: #990000"><</span> ActionController
|
||||
|
||||
cache_page <span style="color: #990000">:</span>list
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> list<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>The first time anyone requestsion products/list, Rails will generate a file
|
||||
called list.html and the webserver will then look for that file before it
|
||||
passes the next request for products/list to your Rails application.</p></div>
|
||||
<div class="para"><p>By default, the page cache directory is set to Rails.public_path (which is
|
||||
usually set to RAILS_ROOT + "/public") and this can be configured by changing
|
||||
the configuration setting Base.cache_public_directory</p></div>
|
||||
<div class="para"><p>The page caching mechanism will automatically add a .html exxtension to
|
||||
requests for pages that do not have an extension to make it easy for the
|
||||
webserver to find those pages and this can be configured by changing the
|
||||
configuration setting Base.page_cache_extension</p></div>
|
||||
<div class="para"><p>In order to expire this page when a new product is added we could extend our
|
||||
example controler like this:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductController <span style="color: #990000"><</span> ActionController
|
||||
|
||||
cache_page <span style="color: #990000">:</span>list
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> list<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
|
||||
expire_page <span style="color: #990000">:</span>action <span style="color: #990000">=></span> <span style="color: #990000">:</span>list
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>If you want a more complicated expiration scheme, you can use cache sweepers
|
||||
to expire cached objects when things change. This is covered in the section on Sweepers.</p></div>
|
||||
<h3 id="_action_caching">1.2. Action Caching</h3>
|
||||
<div class="para"><p>One of the issues with page caching is that you cannot use it for pages that
|
||||
require to restrict access somehow. This is where Action Caching comes in.
|
||||
Action Caching works like Page Caching except for the fact that the incoming
|
||||
web request does go from the webserver to the Rails stack and Action Pack so
|
||||
that before_filters can be run on it before the cache is served, so that
|
||||
authentication and other restrictions can be used while still serving the
|
||||
result of the output from a cached copy.</p></div>
|
||||
<div class="para"><p>Clearing the cache works in the exact same way as with Page Caching.</p></div>
|
||||
<div class="para"><p>Let's say you only wanted authenticated users to edit or create a Product
|
||||
object, but still cache those pages:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductController <span style="color: #990000"><</span> ActionController
|
||||
|
||||
before_filter <span style="color: #990000">:</span>authenticate<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=></span> <span style="color: #990000">[</span> <span style="color: #990000">:</span>edit<span style="color: #990000">,</span> <span style="color: #990000">:</span>create <span style="color: #990000">]</span>
|
||||
cache_page <span style="color: #990000">:</span>list
|
||||
caches_action <span style="color: #990000">:</span>edit
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> list<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
|
||||
expire_page <span style="color: #990000">:</span>action <span style="color: #990000">=></span> <span style="color: #990000">:</span>list
|
||||
expire_action <span style="color: #990000">:</span>action <span style="color: #990000">=></span> <span style="color: #990000">:</span>edit
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>And you can also use :if (or :unless) to pass a Proc that specifies when the
|
||||
action should be cached. Also, you can use :layout ⇒ false to cache without
|
||||
layout so that dynamic information in the layout such as logged in user info
|
||||
or the number of items in the cart can be left uncached. This feature is
|
||||
available as of Rails 2.2.</p></div>
|
||||
<div class="para"><p>[More: more examples? Walk-through of action caching from request to response?
|
||||
Description of Rake tasks to clear cached files? Show example of
|
||||
subdomain caching? Talk about :cache_path, :if and assing blocks/Procs
|
||||
to expire_action?]</p></div>
|
||||
<h3 id="_fragment_caching">1.3. Fragment Caching</h3>
|
||||
<div class="para"><p>Life would be perfect if we could get away with caching the entire contents of
|
||||
a page or action and serving it out to the world. Unfortunately, dynamic web
|
||||
applications usually build pages with a variety of components not all of which
|
||||
have the same caching characteristics. In order to address such a dynamically
|
||||
created page where different parts of the page need to be cached and expired
|
||||
differently Rails provides a mechanism called Fragment caching.</p></div>
|
||||
<div class="para"><p>Fragment caching allows a fragment of view logic to be wrapped in a cache
|
||||
block and served out of the cache store when the next request comes in.</p></div>
|
||||
<div class="para"><p>As an example, if you wanted to show all the orders placed on your website in
|
||||
real time and didn't want to cache that part of the page, but did want to
|
||||
cache the part of the page which lists all products available, you could use
|
||||
this piece of code:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="color: #FF0000"><% Order.find_recent.each do |o| %></span>
|
||||
<span style="color: #FF0000"><%= o.buyer.name %></span> bought <span style="color: #FF0000"><% o.product.name %></span>
|
||||
<span style="color: #FF0000"><% end %></span>
|
||||
|
||||
<span style="color: #FF0000"><% cache do %></span>
|
||||
All available products<span style="color: #990000">:</span>
|
||||
<span style="color: #FF0000"><% Product.find(:all).each do |p| %></span>
|
||||
<span style="color: #FF0000"><%= link_to p.name, product_url(p) %></span>
|
||||
<span style="color: #FF0000"><% end %></span>
|
||||
<span style="color: #FF0000"><% end %></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>The cache block in our example will bind to the action that called it and is
|
||||
written out to the same place as the Action Cache, which means that if you
|
||||
want to cache multiple fragments per action, you should provide an action_path to the cache call:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="color: #FF0000"><% cache(:action =></span> <span style="color: #FF0000">'recent'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action_suffix <span style="color: #990000">=></span> <span style="color: #FF0000">'all_products'</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">%></span>
|
||||
All available products<span style="color: #990000">:</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>and you can expire it using the expire_fragment method, like so:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>expire_fragment<span style="color: #990000">(:</span>controller <span style="color: #990000">=></span> <span style="color: #FF0000">'producst'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=></span> <span style="color: #FF0000">'recent'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action_suffix <span style="color: #990000">=></span> 'all_products<span style="color: #990000">)</span>
|
||||
</tt></pre></div></div>
|
||||
<h3 id="_sweepers">1.4. Sweepers</h3>
|
||||
<div class="para"><p>Cache sweeping is a mechanism which allows you to get around having a ton of
|
||||
expire_{page,action,fragment} calls in your code by moving all the work
|
||||
required to expire cached content into a ActionController::Caching::Sweeper
|
||||
class that is an Observer and looks for changes to an object via callbacks,
|
||||
and when a change occurs it expires the caches associated with that object n
|
||||
an around or after filter.</p></div>
|
||||
<div class="para"><p>Continuing with our Product controller example, we could rewrite it with a
|
||||
sweeper such as the following:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> StoreSweeper <span style="color: #990000"><</span> ActionController<span style="color: #990000">::</span>Caching<span style="color: #990000">::</span>Sweeper
|
||||
observe Product <span style="font-style: italic"><span style="color: #9A1900"># This sweeper is going to keep an eye on the Post model</span></span>
|
||||
|
||||
<span style="font-style: italic"><span style="color: #9A1900"># If our sweeper detects that a Post was created call this</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> after_create<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
|
||||
expire_cache_for<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-style: italic"><span style="color: #9A1900"># If our sweeper detects that a Post was updated call this</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> after_update<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
|
||||
expire_cache_for<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-style: italic"><span style="color: #9A1900"># If our sweeper detects that a Post was deleted call this</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> after_destroy<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
|
||||
expire_cache_for<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
private
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> expire_cache_for<span style="color: #990000">(</span>record<span style="color: #990000">)</span>
|
||||
<span style="font-style: italic"><span style="color: #9A1900"># Expire the list page now that we added a new product</span></span>
|
||||
expire_page<span style="color: #990000">(:</span>controller <span style="color: #990000">=></span> <span style="color: #FF0000">'#{record}'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=></span> <span style="color: #FF0000">'list'</span><span style="color: #990000">)</span>
|
||||
|
||||
<span style="font-style: italic"><span style="color: #9A1900"># Expire a fragment</span></span>
|
||||
expire_fragment<span style="color: #990000">(:</span>controller <span style="color: #990000">=></span> <span style="color: #FF0000">'#{record}'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=></span> <span style="color: #FF0000">'recent'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action_suffix <span style="color: #990000">=></span> <span style="color: #FF0000">'all_products'</span><span style="color: #990000">)</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>Then we add it to our controller to tell it to call the sweeper when certain
|
||||
actions are called. So, if we wanted to expire the cached content for the
|
||||
list and edit actions when the create action was called, we could do the
|
||||
following:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductController <span style="color: #990000"><</span> ActionController
|
||||
|
||||
before_filter <span style="color: #990000">:</span>authenticate<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=></span> <span style="color: #990000">[</span> <span style="color: #990000">:</span>edit<span style="color: #990000">,</span> <span style="color: #990000">:</span>create <span style="color: #990000">]</span>
|
||||
cache_page <span style="color: #990000">:</span>list
|
||||
caches_action <span style="color: #990000">:</span>edit
|
||||
cache_sweeper <span style="color: #990000">:</span>store_sweeper<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=></span> <span style="color: #990000">[</span> <span style="color: #990000">:</span>create <span style="color: #990000">]</span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> list<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
|
||||
expire_page <span style="color: #990000">:</span>action <span style="color: #990000">=></span> <span style="color: #990000">:</span>list
|
||||
expire_action <span style="color: #990000">:</span>action <span style="color: #990000">=></span> <span style="color: #990000">:</span>edit
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<h3 id="_sql_caching">1.5. SQL Caching</h3>
|
||||
<div class="para"><p>Query caching is a Rails feature that caches the result set returned by each
|
||||
query so that if Rails encounters the same query again for that request, it
|
||||
will used the cached result set as opposed to running the query against the
|
||||
database again.</p></div>
|
||||
<div class="para"><p>For example:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductController <span style="color: #990000"><</span> ActionController
|
||||
|
||||
before_filter <span style="color: #990000">:</span>authenticate<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=></span> <span style="color: #990000">[</span> <span style="color: #990000">:</span>edit<span style="color: #990000">,</span> <span style="color: #990000">:</span>create <span style="color: #990000">]</span>
|
||||
cache_page <span style="color: #990000">:</span>list
|
||||
caches_action <span style="color: #990000">:</span>edit
|
||||
cache_sweeper <span style="color: #990000">:</span>store_sweeper<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=></span> <span style="color: #990000">[</span> <span style="color: #990000">:</span>create <span style="color: #990000">]</span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> list
|
||||
<span style="font-style: italic"><span style="color: #9A1900"># Run a find query</span></span>
|
||||
Product<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">)</span>
|
||||
|
||||
<span style="color: #990000">...</span>
|
||||
|
||||
<span style="font-style: italic"><span style="color: #9A1900"># Run the same query again</span></span>
|
||||
Product<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">)</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
|
||||
expire_page <span style="color: #990000">:</span>action <span style="color: #990000">=></span> <span style="color: #990000">:</span>list
|
||||
expire_action <span style="color: #990000">:</span>action <span style="color: #990000">=></span> <span style="color: #990000">:</span>edit
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>In the <em>list</em> action above, the result set returned by the first
|
||||
Product.find(:all) will be cached and will be used to avoid querying the
|
||||
database again the second time that finder is called.</p></div>
|
||||
<div class="para"><p>Query caches are created at the start of an action and destroyed at the end of
|
||||
that action and thus persist only for the duration of the action.</p></div>
|
||||
<h3 id="_cache_stores">1.6. Cache stores</h3>
|
||||
<div class="para"><p>Rails provides different stores for the cached data for action and fragment
|
||||
caches. Page caches are always stored on disk.</p></div>
|
||||
<div class="para"><p>The cache stores provided include:</p></div>
|
||||
<div class="para"><p>1) Memory store: Cached data is stored in the memory allocated to the Rails
|
||||
process, which is fine for WEBrick and for FCGI (if you
|
||||
don't care that each FCGI process holds its own fragment
|
||||
store). It's not suitable for CGI as the process is thrown
|
||||
away at the end of each request. It can potentially also
|
||||
take up a lot of memory since each process keeps all the
|
||||
caches in memory.</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>ActionController<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>cache_store <span style="color: #990000">=</span> <span style="color: #990000">:</span>memory_store
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>2) File store: Cached data is stored on the disk, this is the default store
|
||||
and the default path for this store is: /tmp/cache. Works
|
||||
well for all types of environments and allows all processes
|
||||
running from the same application directory to access the
|
||||
cached content.</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>ActionController<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>cache_store <span style="color: #990000">=</span> <span style="color: #990000">:</span>file_store<span style="color: #990000">,</span> <span style="color: #FF0000">"/path/to/cache/directory"</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>3) DRb store: Cached data is stored in a separate shared DRb process that all
|
||||
servers communicate with. This works for all environments and
|
||||
only keeps one cache around for all processes, but requires
|
||||
that you run and manage a separate DRb process.</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>ActionController<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>cache_store <span style="color: #990000">=</span> <span style="color: #990000">:</span>drb_store<span style="color: #990000">,</span> <span style="color: #FF0000">"druby://localhost:9192"</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>4) MemCached store: Works like DRbStore, but uses Danga's MemCache instead.
|
||||
Requires the ruby-memcache library:
|
||||
gem install ruby-memcache.</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>ActionController<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>cache_store <span style="color: #990000">=</span> <span style="color: #990000">:</span>mem_cache_store<span style="color: #990000">,</span> <span style="color: #FF0000">"localhost"</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>5) Custom store: You can define your own cache store (new in Rails 2.1)</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>ActionController<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>cache_store <span style="color: #990000">=</span> MyOwnStore<span style="color: #990000">.</span>new<span style="color: #990000">(</span><span style="color: #FF0000">"parameter"</span><span style="color: #990000">)</span>
|
||||
</tt></pre></div></div>
|
||||
</div>
|
||||
<h2 id="_advanced_caching">2. Advanced Caching</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>Along with the built-in mechanisms outlined above, a number of excellent
|
||||
plugins exist to help with finer grained control over caching. These include
|
||||
Chris Wanstrath's excellent cache_fu plugin (more info here:
|
||||
<a href="http://errtheblog.com/posts/57-kickin-ass-w-cachefu">http://errtheblog.com/posts/57-kickin-ass-w-cachefu</a>) and Evan Weaver's
|
||||
interlock plugin (more info here:
|
||||
<a href="http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/">http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/</a>). Both
|
||||
of these plugins play nice with memcached and are a must-see for anyone
|
||||
seriously considering optimizing their caching needs.</p></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,901 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Rails Finders</title>
|
||||
<!--[if lt IE 8]>
|
||||
<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
|
||||
<![endif]-->
|
||||
<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<style type="text/css">
|
||||
div#container {
|
||||
max-width: 900px;
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
div#content {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
div#container.notoc {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.notoc div#content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
#content p tt {
|
||||
background: #eeeeee;
|
||||
border: solid 1px #cccccc;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#content dt tt {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-left: 3em;
|
||||
}
|
||||
|
||||
#content dt tt, #content pre tt {
|
||||
background: none;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#content .olist ol {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
#header {
|
||||
position: relative;
|
||||
max-width: 840px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#header.notoc {
|
||||
max-width: 580px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
width: 110px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
div#header h1#site_title {
|
||||
background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
|
||||
position: absolute;
|
||||
width: 392px;
|
||||
height: 55px;
|
||||
left: 145px;
|
||||
top: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#site_title span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#site_title_tagline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul#navMain {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: 97px;
|
||||
left: 145px;
|
||||
}
|
||||
|
||||
.left-floaty, .right-floaty {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.admonitionblock,
|
||||
.tableblock {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.admonitionblock .icon {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .content {
|
||||
border: solid 1px #ffda78;
|
||||
background: #fffebd;
|
||||
padding: 10px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .title {
|
||||
font-size: 140%;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.tableblock table {
|
||||
border: solid 1px #aaaaff;
|
||||
background: #f0f0ff;
|
||||
}
|
||||
|
||||
.tableblock th {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
.tableblock th,
|
||||
.tableblock td {
|
||||
padding: 3px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.sidebarblock {
|
||||
margin-top: 0.25em;
|
||||
margin: 1em;
|
||||
border: solid 1px #ccccbb;
|
||||
padding: 8px;
|
||||
background: #ffffe0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title {
|
||||
font-size: 140%;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-content > .para:last-child > p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header" >
|
||||
<div id="logo">
|
||||
<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
|
||||
</div>
|
||||
|
||||
<h1 id="site_title"><span>Ruby on Rails</span></h1>
|
||||
<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
|
||||
|
||||
<ul id="navMain">
|
||||
<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
|
||||
<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="sidebar">
|
||||
<h2>Chapters</h2>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="#_in_the_beginning_8230">In the beginning…</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_our_models">Our Models</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_database_agnostic">Database Agnostic</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_ids_first_last_and_all">IDs, First, Last and All</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_conditions">Conditions</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_ordering">Ordering</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_selecting_certain_fields">Selecting Certain Fields</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_limit_amp_offset">Limit & Offset</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_group">Group</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_read_only">Read Only</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_lock">Lock</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_making_it_all_work_together">Making It All Work Together</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_eager_loading">Eager Loading</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_dynamic_finders">Dynamic finders</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_finding_by_sql">Finding By SQL</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_working_with_associations">Working with Associations</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_named_scopes">Named Scopes</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_existance_of_objects">Existance of Objects</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_calculations">Calculations</a>
|
||||
<ul>
|
||||
|
||||
<li><a href="#_count">Count</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_with_scope">With Scope</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_credits">Credits</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_change_log">Change Log</a>
|
||||
<ul>
|
||||
|
||||
<li><a href="#_sunday_28_september_2008">Sunday, 28 September 2008</a></li>
|
||||
|
||||
<li><a href="#_wednesday_01_october_2008">Wednesday, 01 October 2008</a></li>
|
||||
|
||||
<li><a href="#_sunday_05_october_2008">Sunday, 05 October 2008</a></li>
|
||||
|
||||
<li><a href="#_monday_06_october_2008">Monday, 06 October 2008</a></li>
|
||||
|
||||
<li><a href="#_thursday_09_october_2008">Thursday, 09 October 2008</a></li>
|
||||
|
||||
<li><a href="#_tuesday_21_october_2008">Tuesday, 21 October 2008</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<h1>Rails Finders</h1>
|
||||
<div id="preamble">
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>This guide is all about the <tt>find</tt> method defined in ActiveRecord::Base, finding on associations, and associated goodness such as named scopes. You will learn how to be a find master.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 id="_in_the_beginning_8230">1. In the beginning…</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>In the beginning there was SQL. SQL looked like this:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> id <span style="color: #990000">=</span> <span style="color: #FF0000">'1'</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">0</span><span style="color: #990000">,</span><span style="color: #993399">1</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">ORDER</span></span> <span style="font-weight: bold"><span style="color: #0000FF">BY</span></span> id <span style="font-weight: bold"><span style="color: #0000FF">DESC</span></span> <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">0</span><span style="color: #990000">,</span><span style="color: #993399">1</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>In Rails you don't usually have to type SQL (unlike other languages) because ActiveRecord is there to help you find your records.</p></div>
|
||||
</div>
|
||||
<h2 id="_our_models">2. Our Models</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>For this guide we have the following models:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
has_one <span style="color: #990000">:</span>address
|
||||
has_one <span style="color: #990000">:</span>mailing_address
|
||||
has_many <span style="color: #990000">:</span>orders
|
||||
has_and_belongs_to_many <span style="color: #990000">:</span>roles
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Address <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
belongs_to <span style="color: #990000">:</span>client
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> MailingAddress <span style="color: #990000"><</span> Address
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
belongs_to <span style="color: #990000">:</span>client<span style="color: #990000">,</span> <span style="color: #990000">:</span>counter_cache <span style="color: #990000">=></span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Role <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
has_and_belongs_to_many <span style="color: #990000">:</span>clients
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
</div>
|
||||
<h2 id="_database_agnostic">3. Database Agnostic</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>ActiveRecord will perform queries on the database for you and is compatible with most database systems (MySQL, PostgreSQL and SQLite to name a few). Regardless of which database system you're using, the ActiveRecord method format will always be the same.</p></div>
|
||||
</div>
|
||||
<h2 id="_ids_first_last_and_all">4. IDs, First, Last and All</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>ActiveRecord::Base has methods defined on it to make interacting with your database and the tables within it much, much easier: find. This method allows you to pass arguments into it to perform certain queries on your database without the need of SQL. If you wanted to find the record with the id of 1, you could type Client.find(1) which would execute this query on your database:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #FF0000">`clients`</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span><span style="color: #FF0000">`clients`</span><span style="color: #990000">.</span><span style="color: #FF0000">`id`</span> <span style="color: #990000">=</span> <span style="color: #993399">1</span><span style="color: #990000">)</span>
|
||||
NOTE<span style="color: #990000">:</span> Please be aware that because this <span style="font-weight: bold"><span style="color: #0000FF">is</span></span> a standard <span style="font-weight: bold"><span style="color: #0000FF">table</span></span> created <span style="font-weight: bold"><span style="color: #0000FF">from</span></span> a migration <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> Rails that the <span style="font-weight: bold"><span style="color: #0000FF">primary</span></span> <span style="font-weight: bold"><span style="color: #0000FF">key</span></span> <span style="font-weight: bold"><span style="color: #0000FF">is</span></span> defaulted <span style="font-weight: bold"><span style="color: #0000FF">to</span></span> <span style="color: #FF0000">'id'</span><span style="color: #990000">.</span> <span style="font-weight: bold"><span style="color: #0000FF">If</span></span> you have specified a different <span style="font-weight: bold"><span style="color: #0000FF">primary</span></span> <span style="font-weight: bold"><span style="color: #0000FF">key</span></span> <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> your migrations<span style="color: #990000">,</span> this <span style="font-weight: bold"><span style="color: #0000FF">is</span></span> what Rails will find <span style="font-weight: bold"><span style="color: #0000FF">on</span></span> when you call the find method<span style="color: #990000">,</span> <span style="font-weight: bold"><span style="color: #0000FF">not</span></span> the id <span style="font-weight: bold"><span style="color: #0000FF">column</span></span><span style="color: #990000">.</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>If you wanted to find clients with id 1 or 2, you call <tt>Client.find([1,2])</tt> or <tt>Client.find(1,2)</tt> and then this will be executed as:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #FF0000">`clients`</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span><span style="color: #FF0000">`clients`</span><span style="color: #990000">.</span><span style="color: #FF0000">`id`</span> <span style="font-weight: bold"><span style="color: #0000FF">IN</span></span> <span style="color: #990000">(</span><span style="color: #993399">1</span><span style="color: #990000">,</span><span style="color: #993399">2</span><span style="color: #990000">))</span>
|
||||
<span style="color: #990000">[</span>source<span style="color: #990000">,</span>txt<span style="color: #990000">]</span>
|
||||
<span style="color: #990000">>></span> Client<span style="color: #990000">.</span>find<span style="color: #990000">(</span><span style="color: #993399">1</span><span style="color: #990000">,</span><span style="color: #993399">2</span><span style="color: #990000">)</span>
|
||||
<span style="color: #990000">=></span> <span style="color: #990000">[</span><span style="font-style: italic"><span style="color: #9A1900">#<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">]</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>Note that if you pass in a list of numbers that the result will be returned as an array, not an object of Client.</p></div>
|
||||
<div class="para"><p>If you wanted to find the first client you would simply type <tt>Client.find(:first)</tt> and that would find the first client created in your clients table:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>>> Client.find(: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">
|
||||
If you were running script/server you may see the following output:
|
||||
</tt></pre></div></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">1</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>Indicating the query that Rails has performed on your database.</p></div>
|
||||
<div class="para"><p>To find the last client you would simply type <tt>Client.find(:last)</tt> and that would find the last client created in your clients table:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>>> 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">
|
||||
</tt></pre></div></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">ORDER</span></span> <span style="font-weight: bold"><span style="color: #0000FF">BY</span></span> clients<span style="color: #990000">.</span>id <span style="font-weight: bold"><span style="color: #0000FF">DESC</span></span> <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">1</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>To find all the clients you would simply type <tt>Client.find(:all)</tt> and that would find all the clients in your clients table:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>>> Client.find(: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">]
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>Alternatively to calling Client.find(:first)/<tt>Client.find(:last)</tt>/<tt>Client.find(:all)</tt>, you could use the class method of <tt>Client.first</tt>/<tt>Client.last</tt>/<tt>Client.all</tt> instead. <tt>Client.first</tt>, <tt>Client.last</tt> and <tt>Client.all</tt> just call their longer counterparts.</p></div>
|
||||
<div class="para"><p>Be aware that <tt>Client.first</tt>/<tt>Client.find(:first)</tt> and <tt>Client.last</tt>/<tt>Client.find(:last)</tt> will both return a single object, where as <tt>Client.all</tt>/<tt>Client.find(:all)</tt> will return an array of Client objects, just as passing in an array of ids to find will do also.</p></div>
|
||||
</div>
|
||||
<h2 id="_conditions">5. Conditions</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>If you'd like to add conditions to your find, you could just specify them in there, just like <tt>Client.find(:first, :conditions ⇒ "orders_count = <em>2</em>")</tt>. Now what if that number could vary, say as a parameter from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like <tt>Client.find(:first, :conditions ⇒ ["orders_count = ?", params[:orders]])</tt>. ActiveRecord will go through the first element in the conditions value and any additional elements will replace the question marks (?) in the first element. If you want to specify two conditions, you can do it like <tt>Client.find(:first, :conditions ⇒ ["orders_count = ? AND locked = ?", params[:orders], false])</tt>. In this example, the first question mark will be replaced with the value in params orders and the second will be replaced with true and this will find the first record in the table that has <em>2</em> as its value for the orders_count field and <em>false</em> for its locked field.</p></div>
|
||||
<div class="para"><p>The reason for doing code like:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>`Client<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>first<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=></span> <span style="color: #990000">[</span><span style="color: #FF0000">"orders_count = ?"</span><span style="color: #990000">,</span> params<span style="color: #990000">[:</span>orders<span style="color: #990000">]])</span>`
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>instead of:</p></div>
|
||||
<div class="para"><p><tt>Client.find(:first, :conditions ⇒ "orders_count = #{params[:orders]}")</tt></p></div>
|
||||
<div class="para"><p>is because of parameter safety. Putting the variable directly into the conditions string will parse the variable <strong>as-is</strong>. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your parameters directly inside the conditions string.</p></div>
|
||||
<div class="para"><p>If you're looking for a range inside of a table for example users created in a certain timeframe you can use the conditions option coupled with the IN sql statement for this. If we had two dates coming in from a controller we could do something like this to look for a range:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=></span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at IN (?)"</span><span style="color: #990000">,</span> <span style="color: #990000">(</span>params<span style="color: #990000">[:</span>start_date<span style="color: #990000">].</span>to_date<span style="color: #990000">)..(</span>params<span style="color: #990000">[:</span>end_date<span style="color: #990000">].</span>to_date<span style="color: #990000">)])</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>This would generate the proper query which is great for small ranges but not so good for larger ranges. For example if you pass in a range of date objects spanning a year that's 365 (or possibly 366, depending on the year) strings it will attempt to match your field against.</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #FF0000">`users`</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span>created_at <span style="font-weight: bold"><span style="color: #0000FF">IN</span></span> <span style="color: #990000">(</span><span style="color: #FF0000">'2007-12-31'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-01'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-02'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-03'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-04'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-05'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-06'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-07'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-08'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-09'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-10'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-11'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-12'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-13'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-14'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-15'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-16'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-17'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-18'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-19'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-20'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-21'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-22'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-23'</span><span style="color: #990000">,...</span>
|
||||
<span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">15</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">16</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">17</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">18</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">19</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">20</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">21</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">22</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">23</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">24</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">25</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">26</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">27</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">28</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">29</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">30</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">31</span><span style="color: #FF0000">'))</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>Things can get <strong>really</strong> messy if you pass in time objects as it will attempt to compare your field to <strong>every second</strong> in that range:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=></span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at IN (?)"</span><span style="color: #990000">,</span> <span style="color: #990000">(</span>params<span style="color: #990000">[:</span>start_date<span style="color: #990000">].</span>to_date<span style="color: #990000">.</span>to_time<span style="color: #990000">)..(</span>params<span style="color: #990000">[:</span>end_date<span style="color: #990000">].</span>to_date<span style="color: #990000">.</span>to_time<span style="color: #990000">)])</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #FF0000">`users`</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span>created_at <span style="font-weight: bold"><span style="color: #0000FF">IN</span></span> <span style="color: #990000">(</span><span style="color: #FF0000">'2007-12-01 00:00:00'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'2007-12-01 00:00:01'</span> <span style="color: #990000">...</span> <span style="color: #FF0000">'2007-12-01 23:59:59'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'2007-12-02 00:00:00'</span><span style="color: #990000">))</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>This could possibly cause your database server to raise an unexpected error, for example MySQL will throw back this error:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Got a packet bigger than 'max_allowed_packet' bytes: <query>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>Where <query> is the actual query used to get that error.</p></div>
|
||||
<div class="para"><p>In this example it would be better to use greater-than and less-than operators in SQL, like so:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>condtions <span style="color: #990000">=></span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at > ? AND created_at < ?"</span><span style="color: #990000">,</span> params<span style="color: #990000">[:</span>start_date<span style="color: #990000">],</span> params<span style="color: #990000">[:</span>end_date<span style="color: #990000">]])</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>You can also use the greater-than-or-equal-to and less-than-or-equal-to like this:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>condtions <span style="color: #990000">=></span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at >= ? AND created_at <= ?"</span><span style="color: #990000">,</span> params<span style="color: #990000">[:</span>start_date<span style="color: #990000">],</span> params<span style="color: #990000">[:</span>end_date<span style="color: #990000">]])</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>Just like in Ruby.</p></div>
|
||||
</div>
|
||||
<h2 id="_ordering">6. Ordering</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>If you're getting a set of records and want to force an order, you can use <tt>Client.find(:all, :order ⇒ "created_at")</tt> which by default will sort the records by ascending order. If you'd like to order it in descending order, just tell it to do that using <tt>Client.find(:all, :order ⇒ "created_at desc")</tt></p></div>
|
||||
</div>
|
||||
<h2 id="_selecting_certain_fields">7. Selecting Certain Fields</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>To select certain fields, you can use the select option like this: <tt>Client.find(:first, :select ⇒ "viewable_by, locked")</tt>. This select option does not use an array of fields, but rather requires you to type SQL-like code. The above code will execute <tt>SELECT viewable_by, locked FROM clients LIMIT 0,1</tt> on your database.</p></div>
|
||||
</div>
|
||||
<h2 id="_limit_amp_offset">8. Limit & Offset</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>If you want to limit the amount of records to a certain subset of all the records retreived you usually use limit for this, sometimes coupled with offset. Limit is the maximum number of records that will be retreived from a query, and offset is the number of records it will start reading from from the first record of the set. Take this code for example:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>limit <span style="color: #990000">=></span> <span style="color: #993399">5</span><span style="color: #990000">)</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>This code will return a maximum of 5 clients and because we've specified no offset it will return the first 5 clients in the table. The SQL it executes will look like this:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">5</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>limit <span style="color: #990000">=></span> <span style="color: #993399">5</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>offset <span style="color: #990000">=></span> <span style="color: #993399">5</span><span style="color: #990000">)</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>This code will return a maximum of 5 clients and because we have specified an offset this time, it will return these records starting from the 5th client in the clients table. The SQL looks like:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">5</span><span style="color: #990000">,</span> <span style="color: #993399">5</span>
|
||||
</tt></pre></div></div>
|
||||
</div>
|
||||
<h2 id="_group">9. Group</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>TODO</p></div>
|
||||
</div>
|
||||
<h2 id="_read_only">10. Read Only</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>Readonly is a find option that you can set in order to make that instance of the record read-only. Any attempt to alter or destroy the record will not succeed, raising an <tt>ActiveRecord::ReadOnlyRecord</tt> error. To set this option, specify it like this:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>first<span style="color: #990000">,</span> <span style="color: #990000">:</span>readonly <span style="color: #990000">=></span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span><span style="color: #990000">)</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>If you assign this record to a variable <tt>client</tt> calling the following code will raise an ActiveRecord::ReadOnlyRecord:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>client <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>first<span style="color: #990000">,</span> <span style="color: #990000">:</span>readonly <span style="color: #990000">=></span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span><span style="color: #990000">)</span>
|
||||
client<span style="color: #990000">.</span>locked <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
|
||||
client<span style="color: #990000">.</span>save
|
||||
</tt></pre></div></div>
|
||||
</div>
|
||||
<h2 id="_lock">11. Lock</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>If you're wanting to stop race conditions for a specific record, say for example you're incrementing a single field for a record you can use the lock option to ensure that the record is updated correctly. It's recommended this be used inside a transaction.</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"></div></div>
|
||||
</div>
|
||||
<h2 id="_making_it_all_work_together">12. Making It All Work Together</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>You can chain these options together in no particular order as ActiveRecord will write the correct SQL for you. If you specify two instances of the same options inside the find statement ActiveRecord will use the latter.</p></div>
|
||||
</div>
|
||||
<h2 id="_eager_loading">13. Eager Loading</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>Eager loading is loading associated records along with any number of records in as few queries as possible. Lets say for example if we wanted to load all the addresses associated with all the clients all in the same query we would use <tt>Client.find(:all, :include ⇒ :address)</tt>. If we wanted to include both the address and mailing address for the client we would use `Client.find(:all), :include ⇒ [:address, :mailing_address]). Inclue will first find the client records and then load the associated address records. Running script/server in one window, and executing the code through script/console in another window, the output should look similar to this:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client <span style="font-weight: bold"><span style="color: #0000FF">Load</span></span> <span style="color: #990000">(</span><span style="color: #993399">0.000383</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients
|
||||
Address <span style="font-weight: bold"><span style="color: #0000FF">Load</span></span> <span style="color: #990000">(</span><span style="color: #993399">0.119770</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> addresses<span style="color: #990000">.*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> addresses <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span>addresses<span style="color: #990000">.</span>client_id <span style="font-weight: bold"><span style="color: #0000FF">IN</span></span> <span style="color: #990000">(</span><span style="color: #993399">13</span><span style="color: #990000">,</span><span style="color: #993399">14</span><span style="color: #990000">))</span>
|
||||
MailingAddress <span style="font-weight: bold"><span style="color: #0000FF">Load</span></span> <span style="color: #990000">(</span><span style="color: #993399">0.001985</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> mailing_addresses<span style="color: #990000">.*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> mailing_addresses <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span>mailing_addresses<span style="color: #990000">.</span>client_id <span style="font-weight: bold"><span style="color: #0000FF">IN</span></span> <span style="color: #990000">(</span><span style="color: #993399">13</span><span style="color: #990000">,</span><span style="color: #993399">14</span><span style="color: #990000">))</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>The numbers <tt>13</tt> and <tt>14</tt> in the above SQL are the ids of the clients gathered from the <tt>Client.find(:all)</tt> query. Rails will then run a query to gather all the addresses and mailing addresses that have a client_id of 13 or 14. Although this is done in 3 queries, this is more efficient than not eager loading because without eager loading it would run a query for every time you called <tt>address</tt> or <tt>mailing_address</tt> on one of the objects in the clients array, which may lead to performance issues if you're loading a large number of records at once.</p></div>
|
||||
<div class="para"><p>An alternative (and more efficient) way to do eager loading is to use the joins option. For example if we wanted to get all the addresses for a client we would do <tt>Client.find(:all, :joins ⇒ :address)</tt> and if we wanted to find the address and mailing address for that client we would do <tt>Client.find(:all, :joins ⇒ [:address, :mailing_address])</tt>. This is more efficient because it does all the SQL in one query, as shown by this example:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="color: #FF0000">`Client Load (0.000455) SELECT clients.* FROM clients INNER JOIN addresses ON addresses.client_id = client.id INNER JOIN mailing_addresses ON mailing_addresses.client_id = client.id</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>This query is more efficent, but there's a gotcha. If you have a client who does not have an address or a mailing address they will not be returned in this query at all. If you have any association as an optional association, you may want to use include rather than joins.</p></div>
|
||||
<div class="para"><p>When using eager loading you can specify conditions for the columns of the tables inside the eager loading to get back a smaller subset. If, for example, you want to find a client and all their orders within the last two weeks you could use eager loading with conditions for this:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"></div></div>
|
||||
</div>
|
||||
<h2 id="_dynamic_finders">14. Dynamic finders</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>With every field (also known as an attribute) you define in your table, ActiveRecord provides finder methods for these. If you have a field called <tt>name</tt> on your Client model for example, you get <tt>find_by_name</tt> and <tt>find_all_by_name</tt> for free from ActiveRecord. If you have also have a <tt>locked</tt> field on the client model, you also get <tt>find_by_locked</tt> and <tt>find_all_by_locked</tt>. If you want to find both by name and locked, you can chain these finders together by simply typing and between the fields for example <tt>Client.find_by_name_and_locked(<em>Ryan</em>, true)</tt>. These finders are an excellent alternative to using the conditions option, mainly because it's shorter to type <tt>find_by_name(params[:name])</tt> than it is to type <tt>find(:first, :conditions ⇒ ["name = ?", params[:name]])</tt>.</p></div>
|
||||
<div class="para"><p>There's another set of dynamic finders that let you find or create/initialize objects if they aren't find. These work in a similar fashion to the other finders and can be used like <tt>find_or_create_by_name(params[:name])</tt>. Using this will firstly perform a find and then create if the find returns nil, the SQL looks like this for <tt>Client.find_or_create_by_name(<em>Ryan</em>)</tt>:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #FF0000">`clients`</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span><span style="color: #FF0000">`clients`</span><span style="color: #990000">.</span><span style="color: #FF0000">`name`</span> <span style="color: #990000">=</span> <span style="color: #FF0000">'Ryan'</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">1</span>
|
||||
BEGIN
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">INSERT</span></span> <span style="font-weight: bold"><span style="color: #0000FF">INTO</span></span> <span style="color: #FF0000">`clients`</span> <span style="color: #990000">(</span><span style="color: #FF0000">`name`</span><span style="color: #990000">,</span> <span style="color: #FF0000">`updated_at`</span><span style="color: #990000">,</span> <span style="color: #FF0000">`created_at`</span><span style="color: #990000">,</span> <span style="color: #FF0000">`orders_count`</span><span style="color: #990000">,</span> <span style="color: #FF0000">`locked`</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">VALUES</span></span><span style="color: #990000">(</span><span style="color: #FF0000">'Ryan'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'2008-09-28 15:39:12'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'2008-09-28 15:39:12'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'0'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'0'</span><span style="color: #990000">)</span>
|
||||
COMMIT
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p><tt>find_or_create</tt>'s sibling, find_or_initialize, will find an object and if it does not exist will call <tt>new</tt> with the parameters you passed in. For example:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>client <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>find_or_initialize_by_name<span style="color: #990000">(</span><span style="color: #FF0000">'Ryan'</span><span style="color: #990000">)</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>will either assign an existing client object with the name <em>Ryan</em> to the client local variable, or initialize new object similar to calling <tt>Client.new(:name ⇒ <em>Ryan</em>)</tt>. From here, you can modify other fields in client by calling the attribute setters on it: <tt>client.locked = true</tt> and when you want to write it to the database just call <tt>save</tt> on it.</p></div>
|
||||
</div>
|
||||
<h2 id="_finding_by_sql">15. Finding By SQL</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>If you'd like to use your own SQL to find records a table you can use <tt>find_by_sql</tt>. <tt>find_by_sql</tt> will return an array of objects even if it only returns a single record in it's call to the database. For example you could run this query:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>find_by_sql<span style="color: #990000">(</span><span style="color: #FF0000">"SELECT * FROM clients INNER JOIN orders ON clients.id = orders.client_id ORDER clients.created_at desc"</span><span style="color: #990000">)</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p><tt>find_by_sql</tt> provides you with a simple way of making custom calls to the database and converting those to objects.</p></div>
|
||||
</div>
|
||||
<h2 id="_working_with_associations">16. Working with Associations</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>When you define a has_many association on a model you get the find method and dynamic finders also on that association. This is helpful for finding associated records within the scope of an exisiting record, for example finding all the orders for a client that have been sent and not received by doing something like <tt>Client.find(params[:id]).orders.find_by_sent_and_received(true, false)</tt>. Having this find method available on associations is extremely helpful when using nested controllers.</p></div>
|
||||
</div>
|
||||
<h2 id="_named_scopes">17. Named Scopes</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>In this section we'll cover adding named scopes to the models in the application. Let's say we want to find all clients who are male we would use this code:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
named_scope <span style="color: #990000">:</span>males<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=></span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>gender <span style="color: #990000">=></span> <span style="color: #FF0000">"male"</span> <span style="color: #FF0000">}</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>And we could call it like <tt>Client.males</tt> to get all the clients who are male.</p></div>
|
||||
<div class="para"><p>If we wanted to find all the clients who are active, we could use this:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
named_scope <span style="color: #990000">:</span>active<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=></span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>active <span style="color: #990000">=></span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="color: #FF0000">}</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>We would call this new named_scope by doing <tt>Client.active</tt> and this will do the same query as if we just used <tt>Client.find(:all, :conditions ⇒ ["active = ?", true])</tt>. Please be aware that the conditions syntax in named_scope and find is different and the two are not interchangeable. If you want to find the first client within this named scope you could do <tt>Client.active.first</tt>.</p></div>
|
||||
<div class="para"><p>and then if we wanted to find all the clients who are active and male we could stack the named scopes like this:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>males<span style="color: #990000">.</span>active
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>If you would then like to do a <tt>find</tt> on that subset of clients, you can. Just like an association, named scopes allow you to call <tt>find</tt> on a set of records:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>males<span style="color: #990000">.</span>active<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=></span> <span style="color: #990000">[</span><span style="color: #FF0000">"age > ?"</span><span style="color: #990000">,</span> params<span style="color: #990000">[:</span>age<span style="color: #990000">]])</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>Now observe the following code:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
named_scope <span style="color: #990000">:</span>recent<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=></span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>created_at <span style="color: #990000">></span> <span style="color: #993399">2</span><span style="color: #990000">.</span>weeks<span style="color: #990000">.</span>ago <span style="color: #FF0000">}</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>What we see here is what looks to be a standard named scope that defines a method called recent which gathers all records created any time between now and 2 weeks ago. That's correct for the first time the model is loaded but for any time after that, <tt>2.weeks.ago</tt> is set to that same value, so you will consistently get records from a certain date until your model is reloaded by something like your application restarting. The way to fix this is to put the code in a lambda block:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
named_scope <span style="color: #990000">:</span>recent<span style="color: #990000">,</span> lambda <span style="color: #FF0000">{</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=></span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at > ?"</span><span style="color: #990000">,</span> <span style="color: #993399">2</span><span style="color: #990000">.</span>weeks<span style="color: #990000">.</span>ago<span style="color: #990000">]</span> <span style="color: #FF0000">}</span> <span style="color: #FF0000">}</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>And now every time the recent named scope is called, because it's wrapped in a lambda block this code will be parsed every time so you'll get actually 2 weeks ago from the code execution, not 2 weeks ago from the time the model was loaded.</p></div>
|
||||
<div class="para"><p>In a named scope you can use <tt>:include</tt> and <tt>:joins</tt> options just like in find.</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
named_scope <span style="color: #990000">:</span>active_within_2_weeks<span style="color: #990000">,</span> <span style="color: #990000">:</span>joins <span style="color: #990000">=></span> <span style="color: #990000">:</span>order<span style="color: #990000">,</span> lambda <span style="color: #FF0000">{</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=></span> <span style="color: #990000">[</span><span style="color: #FF0000">"orders.created_at > ?"</span><span style="color: #990000">,</span> <span style="color: #993399">2</span><span style="color: #990000">.</span>weeks<span style="color: #990000">.</span>ago<span style="color: #990000">]</span> <span style="color: #FF0000">}</span> <span style="color: #FF0000">}</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>This method called as <tt>Client.active_within_2_weeks</tt> will return all clients who have placed orders in the past 2 weeks.</p></div>
|
||||
<div class="para"><p>If you want to pass a named scope a compulsory argument, just specify it as a block parameter like this:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
named_scope <span style="color: #990000">:</span>recent<span style="color: #990000">,</span> lambda <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>time<span style="color: #990000">|</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=></span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at > ?"</span><span style="color: #990000">,</span> time<span style="color: #990000">]</span> <span style="color: #FF0000">}</span> <span style="color: #FF0000">}</span> <span style="color: #FF0000">}</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>This will work if we call <tt>Client.recent(2.weeks.ago)</tt> but not if we call <tt>Client.recent</tt>. If we want to add an optional argument for this, we have to use the splat operator as the block's parameter.</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
named_scope <span style="color: #990000">:</span>recent<span style="color: #990000">,</span> lambda <span style="color: #FF0000">{</span> <span style="color: #990000">|*</span>args<span style="color: #990000">|</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=></span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at > ?"</span><span style="color: #990000">,</span> args<span style="color: #990000">.</span>first <span style="color: #990000">||</span> <span style="color: #993399">2</span><span style="color: #990000">.</span>weeks<span style="color: #990000">.</span>ago<span style="color: #990000">]</span> <span style="color: #FF0000">}</span> <span style="color: #FF0000">}</span> <span style="color: #FF0000">}</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>This will work with <tt>Client.recent(2.weeks.ago)</tt> and <tt>Client.recent</tt> with the latter always returning records with a created_at date between right now and 2 weeks ago.</p></div>
|
||||
<div class="para"><p>Remember that named scopes are stackable, so you will be able to do <tt>Client.recent(2.weeks.ago).unlocked</tt> to find all clients created between right now and 2 weeks ago and have their locked field set to false.</p></div>
|
||||
</div>
|
||||
<h2 id="_existance_of_objects">18. Existance of Objects</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>If you simply want to check for the existance of the object there's a method called <tt>exists?</tt>. This method will query the database using the same query as find, but instead of returning an object or collection of objects it will return either true or false.</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>exists?<span style="color: #990000">(</span><span style="color: #993399">1</span><span style="color: #990000">)</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>The above code will check for the existance of a clients table record with the id of 1 and return true if it exists.</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>exists?<span style="color: #990000">(</span><span style="color: #993399">1</span><span style="color: #990000">,</span><span style="color: #993399">2</span><span style="color: #990000">,</span><span style="color: #993399">3</span><span style="color: #990000">)</span>
|
||||
<span style="font-style: italic"><span style="color: #9A1900"># or</span></span>
|
||||
Client<span style="color: #990000">.</span>exists?<span style="color: #990000">([</span><span style="color: #993399">1</span><span style="color: #990000">,</span><span style="color: #993399">2</span><span style="color: #990000">,</span><span style="color: #993399">3</span><span style="color: #990000">])</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p><tt>exists?</tt> also takes multiple ids, as shown by the above code, but the catch is that it will return true if any one of those records exists.</p></div>
|
||||
<div class="para"><p>Further more, <tt>exists</tt> takes a <tt>conditions</tt> option much like find:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>exists?<span style="color: #990000">(:</span>conditions <span style="color: #990000">=></span> <span style="color: #FF0000">"first_name = 'Ryan'"</span><span style="color: #990000">)</span>
|
||||
</tt></pre></div></div>
|
||||
</div>
|
||||
<h2 id="_calculations">19. Calculations</h2>
|
||||
<div class="sectionbody">
|
||||
<h3 id="_count">19.1. Count</h3>
|
||||
<div class="para"><p>If you want to see how many records are in your models table you could call <tt>Client.count</tt> and that will return the number. If you want to be more specific and find all the clients with their age present in the database you can use <tt>Client.count(:age)</tt>.</p></div>
|
||||
<div class="para"><p><tt>count</tt> takes conditions much in the same way <tt>exists?</tt> does:</p></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>Client<span style="color: #990000">.</span>count<span style="color: #990000">(:</span>conditions <span style="color: #990000">=></span> <span style="color: #FF0000">"first_name = 'Ryan'"</span><span style="color: #990000">)</span>
|
||||
</tt></pre></div></div>
|
||||
<div class="exampleblock">
|
||||
<div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> count<span style="color: #990000">(*)</span> <span style="font-weight: bold"><span style="color: #0000FF">AS</span></span> count_all <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #FF0000">`clients`</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span>first_name <span style="color: #990000">=</span> <span style="color: #993399">1</span><span style="color: #990000">)</span>
|
||||
</tt></pre></div></div>
|
||||
</div>
|
||||
<h2 id="_with_scope">20. With Scope</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>TODO</p></div>
|
||||
</div>
|
||||
<h2 id="_credits">21. Credits</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>Thanks to Ryan Bates for his awesome screencast on named scope #108. The information within the named scope section is intentionally similar to it, and without the cast may have not been possible.</p></div>
|
||||
<div class="para"><p>Thanks to Mike Gunderloy for his tips on creating this guide.</p></div>
|
||||
</div>
|
||||
<h2 id="_change_log">22. Change Log</h2>
|
||||
<div class="sectionbody">
|
||||
<h3 id="_sunday_28_september_2008">22.1. Sunday, 28 September 2008</h3>
|
||||
<div class="olist"><ol>
|
||||
<li>
|
||||
<p>
|
||||
Changed "In Rails you don't have to type SQL" to "In Rails you don't usually have to type SQL"
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Inserted paragraph in dynamic finders about find_or_create and find_or_initialize
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Extended "First, Last, All" section.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Renamed "First, Last & All" to "IDs, First, Last and All"
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Added finding by id and passing in ids to "IDs, First, Last and All"
|
||||
</p>
|
||||
</li>
|
||||
</ol></div>
|
||||
<h3 id="_wednesday_01_october_2008">22.2. Wednesday, 01 October 2008</h3>
|
||||
<div class="olist"><ol>
|
||||
<li>
|
||||
<p>
|
||||
Did section on limit and offset, as well as section on readonly.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Altered formatting so it doesn't look bad.
|
||||
</p>
|
||||
</li>
|
||||
</ol></div>
|
||||
<h3 id="_sunday_05_october_2008">22.3. Sunday, 05 October 2008</h3>
|
||||
<div class="olist"><ol>
|
||||
<li>
|
||||
<p>
|
||||
Extended conditions section to include IN and using operators inside the conditions.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Extended conditions section to include paragraph and example of parameter safety.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Added TODO sections.
|
||||
</p>
|
||||
</li>
|
||||
</ol></div>
|
||||
<h3 id="_monday_06_october_2008">22.4. Monday, 06 October 2008</h3>
|
||||
<div class="olist"><ol>
|
||||
<li>
|
||||
<p>
|
||||
Added section in Eager Loading about using conditions on tables that are not the model's own.
|
||||
</p>
|
||||
</li>
|
||||
</ol></div>
|
||||
<h3 id="_thursday_09_october_2008">22.5. Thursday, 09 October 2008</h3>
|
||||
<div class="olist"><ol>
|
||||
<li>
|
||||
<p>
|
||||
Wrote section about lock option and tidied up "Making it all work together" section.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Added section on using count.
|
||||
</p>
|
||||
</li>
|
||||
</ol></div>
|
||||
<h3 id="_tuesday_21_october_2008">22.6. Tuesday, 21 October 2008</h3>
|
||||
<div class="olist"><ol>
|
||||
<li>
|
||||
<p>
|
||||
Extended named scope guide by adding :include and :joins and find sub-sections.
|
||||
</p>
|
||||
</li>
|
||||
</ol></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,570 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Rails form helpers</title>
|
||||
<!--[if lt IE 8]>
|
||||
<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
|
||||
<![endif]-->
|
||||
<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<style type="text/css">
|
||||
div#container {
|
||||
max-width: 900px;
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
div#content {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
div#container.notoc {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.notoc div#content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
#content p tt {
|
||||
background: #eeeeee;
|
||||
border: solid 1px #cccccc;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#content dt tt {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-left: 3em;
|
||||
}
|
||||
|
||||
#content dt tt, #content pre tt {
|
||||
background: none;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#content .olist ol {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
#header {
|
||||
position: relative;
|
||||
max-width: 840px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#header.notoc {
|
||||
max-width: 580px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
width: 110px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
div#header h1#site_title {
|
||||
background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
|
||||
position: absolute;
|
||||
width: 392px;
|
||||
height: 55px;
|
||||
left: 145px;
|
||||
top: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#site_title span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#site_title_tagline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul#navMain {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: 97px;
|
||||
left: 145px;
|
||||
}
|
||||
|
||||
.left-floaty, .right-floaty {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.admonitionblock,
|
||||
.tableblock {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.admonitionblock .icon {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .content {
|
||||
border: solid 1px #ffda78;
|
||||
background: #fffebd;
|
||||
padding: 10px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .title {
|
||||
font-size: 140%;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.tableblock table {
|
||||
border: solid 1px #aaaaff;
|
||||
background: #f0f0ff;
|
||||
}
|
||||
|
||||
.tableblock th {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
.tableblock th,
|
||||
.tableblock td {
|
||||
padding: 3px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.sidebarblock {
|
||||
margin-top: 0.25em;
|
||||
margin: 1em;
|
||||
border: solid 1px #ccccbb;
|
||||
padding: 8px;
|
||||
background: #ffffe0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title {
|
||||
font-size: 140%;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-content > .para:last-child > p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header" >
|
||||
<div id="logo">
|
||||
<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
|
||||
</div>
|
||||
|
||||
<h1 id="site_title"><span>Ruby on Rails</span></h1>
|
||||
<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
|
||||
|
||||
<ul id="navMain">
|
||||
<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
|
||||
<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="sidebar">
|
||||
<h2>Chapters</h2>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="#_basic_forms">Basic forms</a>
|
||||
<ul>
|
||||
|
||||
<li><a href="#_generic_search_form">Generic search form</a></li>
|
||||
|
||||
<li><a href="#_multiple_hashes_in_form_helper_attributes">Multiple hashes in form helper attributes</a></li>
|
||||
|
||||
<li><a href="#_checkboxes_radio_buttons_and_other_controls">Checkboxes, radio buttons and other controls</a></li>
|
||||
|
||||
<li><a href="#_how_do_forms_with_put_or_delete_methods_work">How do forms with PUT or DELETE methods work?</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_forms_that_deal_with_model_attributes">Forms that deal with model attributes</a>
|
||||
<ul>
|
||||
|
||||
<li><a href="#_relying_on_record_identification">Relying on record identification</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<h1>Rails form helpers</h1>
|
||||
<div id="preamble">
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>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.</p></div>
|
||||
<div class="para"><p>In this guide we will:</p></div>
|
||||
<div class="ilist"><ul>
|
||||
<li>
|
||||
<p>
|
||||
Create search forms and similar kind of generic forms not representing any specific model in your application;
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Make model-centric forms for creation and editing of specific database records;
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Generate select boxes from multiple types of data;
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Learn what makes a file upload form different;
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Build complex, multi-model forms.
|
||||
</p>
|
||||
</li>
|
||||
</ul></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/note.png" alt="Note" />
|
||||
</td>
|
||||
<td class="content">This guide is not intended to be a complete documentation of available form helpers and their arguments. Please visit <a href="http://api.rubyonrails.org/">the Rails API documentation</a> for a complete reference.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 id="_basic_forms">1. Basic forms</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>The most basic form helper is <tt>form_tag</tt>.</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt><% form_tag do %>
|
||||
Form contents
|
||||
<% end %></tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>When called without arguments like this, it creates a form element that has the current page for action attribute and "POST" as method (some line breaks added for readability):</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Example: Sample rendering of <tt>form_tag</tt></div>
|
||||
<div class="content">
|
||||
<pre><tt><form action="/home/index" method="post">
|
||||
<div style="margin:0;padding:0">
|
||||
<input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
|
||||
</div>
|
||||
Form contents
|
||||
</form></tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>If you carefully observe this output, you can see that the helper generated something we didn't specify: a <tt>div</tt> element with a hidden input inside. This is a security feature of Rails called <strong>cross-site request forgery protection</strong> and form helpers generate it for every form which action isn't "GET" (provided that this security feature is enabled).</p></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/note.png" alt="Note" />
|
||||
</td>
|
||||
<td class="content">Throughout this guide, this <tt>div</tt> with the hidden input will be stripped away to have clearer code samples.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<h3 id="_generic_search_form">1.1. Generic search form</h3>
|
||||
<div class="para"><p>Probably the most minimal form often seen on the web is a search form with a single text input for search terms. This form consists of:</p></div>
|
||||
<div class="olist"><ol>
|
||||
<li>
|
||||
<p>
|
||||
a form element with "GET" method,
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
a label for the input,
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
a text input element, and
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
a submit element.
|
||||
</p>
|
||||
</li>
|
||||
</ol></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/important.png" alt="Important" />
|
||||
</td>
|
||||
<td class="content">Always use "GET" as the method for search forms. Benefits are many: users are able to bookmark a specific search and get back to it; browsers cache results of "GET" requests, but not "POST"; and other.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>To create that, we will use <tt>form_tag</tt>, <tt>label_tag</tt>, <tt>text_field_tag</tt> and <tt>submit_tag</tt>, respectively.</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Example: A basic search form</div>
|
||||
<div class="content">
|
||||
<pre><tt><% form_tag(search_path, :method => "get") do %>
|
||||
<%= label_tag(:q, "Search for:") %>
|
||||
<%= text_field_tag(:q) %>
|
||||
<%= submit_tag("Search") %>
|
||||
<% end %></tt></pre>
|
||||
</div></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/tip.png" alt="Tip" />
|
||||
</td>
|
||||
<td class="content">
|
||||
<div class="para"><p><tt>search_path</tt> can be a named route specified in "routes.rb":</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt>map.search "search", :controller => "search"</tt></pre>
|
||||
</div></div>
|
||||
</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>The above view code will result in the following markup:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Example: Search form HTML</div>
|
||||
<div class="content">
|
||||
<pre><tt><form action="/search" method="get">
|
||||
<label for="q">Search for:</label>
|
||||
<input id="q" name="q" type="text" />
|
||||
<input name="commit" type="submit" value="Search" />
|
||||
</form></tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>Besides <tt>text_field_tag</tt> and <tt>submit_tag</tt>, there is a similar helper for <em>every</em> form control in HTML.</p></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/tip.png" alt="Tip" />
|
||||
</td>
|
||||
<td class="content">For every form input, an ID attribute is generated from its name ("q" in our example). These IDs can be very useful for CSS styling or manipulation of form controls with JavaScript.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<h3 id="_multiple_hashes_in_form_helper_attributes">1.2. Multiple hashes in form helper attributes</h3>
|
||||
<div class="para"><p>By now we've seen that the <tt>form_tag</tt> helper accepts 2 arguments: the path for the action attribute and an options hash for parameters (like <tt>:method</tt>).</p></div>
|
||||
<div class="para"><p>Identical to the <tt>link_to</tt> helper, the path argument doesn't have to be given as string or a named route. It can be a hash of URL parameters that Rails' routing mechanism will turn into a valid URL. Still, we cannot simply write this:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Example: A bad way to pass multiple hashes as method arguments</div>
|
||||
<div class="content">
|
||||
<pre><tt>form_tag(:controller => "people", :action => "search", :method => "get")
|
||||
# => <form action="/people/search?method=get" method="post"></tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>Here we wanted to pass two hashes, but the Ruby interpreter sees only one hash, so Rails will construct a URL that we didn't want. The solution is to delimit the first hash (or both hashes) with curly brackets:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Example: The correct way of passing multiple hashes as arguments</div>
|
||||
<div class="content">
|
||||
<pre><tt>form_tag({:controller => "people", :action => "search"}, :method => "get")
|
||||
# => <form action="/people/search" method="get"></tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>This is a common pitfall when using form helpers, since many of them accept multiple hashes. So in future, if a helper produces unexpected output, make sure that you have delimited the hash parameters properly.</p></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/warning.png" alt="Warning" />
|
||||
</td>
|
||||
<td class="content">Do not delimit the second hash without doing so with the first hash, otherwise your method invocation will result in an ugly <tt>expecting tASSOC</tt> syntax error.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<h3 id="_checkboxes_radio_buttons_and_other_controls">1.3. Checkboxes, radio buttons and other controls</h3>
|
||||
<div class="para"><p>Checkboxes are form controls that give the user a set of options they can enable or disable:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt><%= check_box_tag(:pet_dog) %>
|
||||
<%= label_tag(:pet_dog, "I own a dog") %>
|
||||
<%= check_box_tag(:pet_cat) %>
|
||||
<%= label_tag(:pet_cat, "I own a cat") %>
|
||||
|
||||
output:
|
||||
|
||||
<input id="pet_dog" name="pet_dog" type="checkbox" value="1" />
|
||||
<label for="pet_dog">I own a dog</label>
|
||||
<input id="pet_cat" name="pet_cat" type="checkbox" value="1" />
|
||||
<label for="pet_cat">I own a cat</label></tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>Radio buttons, while similar to checkboxes, are controls that specify a set of options in which they are mutually exclusive (user can only pick one):</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt><%= radio_button_tag(:age, "child") %>
|
||||
<%= label_tag(:age_child, "I am younger than 21") %>
|
||||
<%= radio_button_tag(:age, "adult") %>
|
||||
<%= label_tag(:age_adult, "I'm over 21") %>
|
||||
|
||||
output:
|
||||
|
||||
<input id="age_child" name="age" type="radio" value="child" />
|
||||
<label for="age_child">I am younger than 21</label>
|
||||
<input id="age_adult" name="age" type="radio" value="adult" />
|
||||
<label for="age_adult">I'm over 21</label></tt></pre>
|
||||
</div></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/important.png" alt="Important" />
|
||||
</td>
|
||||
<td class="content">Always use labels for each checkbox and radio button. They associate text with a specific option, while also providing a larger clickable region.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>Other form controls we might mention are the text area, password input and hidden input:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt><%= text_area_tag(:message, "Hi, nice site", :size => "24x6") %>
|
||||
<%= password_field_tag(:password) %>
|
||||
<%= hidden_field_tag(:parent_id, "5") %>
|
||||
|
||||
output:
|
||||
|
||||
<textarea id="message" name="message" cols="24" rows="6">Hi, nice site</textarea>
|
||||
<input id="password" name="password" type="password" />
|
||||
<input id="parent_id" name="parent_id" type="hidden" value="5" /></tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>Hidden inputs are not shown to the user, but they hold data same as any textual input. Values inside them can be changed with JavaScript.</p></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/tip.png" alt="Tip" />
|
||||
</td>
|
||||
<td class="content">If you're using password input fields (for any purpose), you might want to prevent their values showing up in application logs by activating <tt>filter_parameter_logging(:password)</tt> in your ApplicationController.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<h3 id="_how_do_forms_with_put_or_delete_methods_work">1.4. How do forms with PUT or DELETE methods work?</h3>
|
||||
<div class="para"><p>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 <em>don't support</em> methods other than "GET" and "POST" when it comes to submitting forms. How does this work, then?</p></div>
|
||||
<div class="para"><p>Rails works around this issue by emulating other methods over POST with a hidden input named <tt>"<em>method"</tt> that is set to reflect the _real</em> method:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt>form_tag(search_path, :method => "put")
|
||||
|
||||
output:
|
||||
|
||||
<form action="/search" method="post">
|
||||
<div style="margin:0;padding:0">
|
||||
<input name="_method" type="hidden" value="put" />
|
||||
<input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
|
||||
</div>
|
||||
...</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>When parsing POSTed data, Rails will take into account the special <tt>"_method"</tt> parameter and act as if the HTTP method was the one specified inside it ("PUT" in this example).</p></div>
|
||||
</div>
|
||||
<h2 id="_forms_that_deal_with_model_attributes">2. Forms that deal with model attributes</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>When we're dealing with an actual model, we will use a different set of form helpers and have Rails take care of some details in the background. In the following examples we will handle an Article model. First, let us have the controller create one:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Example: articles_controller.rb</div>
|
||||
<div class="content">
|
||||
<pre><tt>def new
|
||||
@article = Article.new
|
||||
end</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>Now we switch to the view. The first thing to remember is that we should use <tt>form_for</tt> helper instead of <tt>form_tag</tt>, and that we should pass the model name and object as arguments:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Example: articles/new.html.erb</div>
|
||||
<div class="content">
|
||||
<pre><tt><% form_for :article, @article, :url => { :action => "create" } do |f| %>
|
||||
<%= f.text_field :title %>
|
||||
<%= f.text_area :body, :size => "60x12" %>
|
||||
<%= submit_tag "Create" %>
|
||||
<% end %></tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>There are a few things to note here:</p></div>
|
||||
<div class="olist"><ol>
|
||||
<li>
|
||||
<p>
|
||||
<tt>:article</tt> is the name of the model and <tt>@article</tt> is our record.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
The URL for the action attribute is passed as a parameter named <tt>:url</tt>.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
The <tt>form_for</tt> method yields <strong>a form builder</strong> object (the <tt>f</tt> variable).
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Methods to create form controls are called <strong>on</strong> the form builder object <tt>f</tt> and <strong>without</strong> the <tt>"_tag"</tt> suffix (so <tt>text_field_tag</tt> becomes <tt>f.text_field</tt>).
|
||||
</p>
|
||||
</li>
|
||||
</ol></div>
|
||||
<div class="para"><p>The resulting HTML is:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt><form action="/articles/create" method="post">
|
||||
<input id="article_title" name="article[title]" size="30" type="text" />
|
||||
<textarea id="article_body" name="article[body]" cols="60" rows="12"></textarea>
|
||||
<input name="commit" type="submit" value="Create" />
|
||||
</form></tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>A nice thing about <tt>f.text_field</tt> and other helper methods is that they will pre-fill the form control with the value read from the corresponding attribute in the model. For example, if we created the article instance by supplying an initial value for the title in the controller:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt>@article = Article.new(:title => "Rails makes forms easy")</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>… the corresponding input will be rendered with a value:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt><input id="post_title" name="post[title]" size="30" type="text" value="Rails makes forms easy" /></tt></pre>
|
||||
</div></div>
|
||||
<h3 id="_relying_on_record_identification">2.1. Relying on record identification</h3>
|
||||
<div class="para"><p>In the previous chapter we handled the Article model. This model is directly available to users of our application and, following the best practices for developing with Rails, we should declare it <strong>a resource</strong>.</p></div>
|
||||
<div class="para"><p>When dealing with RESTful resources, our calls to <tt>form_for</tt> can get significantly easier if we rely on <strong>record identification</strong>. In short, we can just pass the model instance and have Rails figure out model name and the rest:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt>## Creating a new article
|
||||
# long-style:
|
||||
form_for(:article, @article, :url => articles_path)
|
||||
# same thing, short-style (record identification gets used):
|
||||
form_for(@article)
|
||||
|
||||
## Editing an existing article
|
||||
# long-style:
|
||||
form_for(:article, @article, :url => article_path(@article), :method => "put")
|
||||
# short-style:
|
||||
form_for(@article)</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>Notice how the short-style <tt>form_for</tt> invocation is conveniently the same, regardless of the record being new or existing. Record identification is smart enough to figure out if the record is new by asking <tt>record.new_record?</tt>.</p></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/warning.png" alt="Warning" />
|
||||
</td>
|
||||
<td class="content">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, <tt>:url</tt> and <tt>:method</tt> explicitly.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,381 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Ruby on Rails guides</title>
|
||||
<!--[if lt IE 8]>
|
||||
<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
|
||||
<![endif]-->
|
||||
<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<style type="text/css">
|
||||
div#container {
|
||||
max-width: 900px;
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
div#content {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
div#container.notoc {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.notoc div#content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
#content p tt {
|
||||
background: #eeeeee;
|
||||
border: solid 1px #cccccc;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#content dt tt {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-left: 3em;
|
||||
}
|
||||
|
||||
#content dt tt, #content pre tt {
|
||||
background: none;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#content .olist ol {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
#header {
|
||||
position: relative;
|
||||
max-width: 840px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#header.notoc {
|
||||
max-width: 580px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
width: 110px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
div#header h1#site_title {
|
||||
background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
|
||||
position: absolute;
|
||||
width: 392px;
|
||||
height: 55px;
|
||||
left: 145px;
|
||||
top: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#site_title span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#site_title_tagline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul#navMain {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: 97px;
|
||||
left: 145px;
|
||||
}
|
||||
|
||||
.left-floaty, .right-floaty {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.admonitionblock,
|
||||
.tableblock {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.admonitionblock .icon {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .content {
|
||||
border: solid 1px #ffda78;
|
||||
background: #fffebd;
|
||||
padding: 10px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .title {
|
||||
font-size: 140%;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.tableblock table {
|
||||
border: solid 1px #aaaaff;
|
||||
background: #f0f0ff;
|
||||
}
|
||||
|
||||
.tableblock th {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
.tableblock th,
|
||||
.tableblock td {
|
||||
padding: 3px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.sidebarblock {
|
||||
margin-top: 0.25em;
|
||||
margin: 1em;
|
||||
border: solid 1px #ccccbb;
|
||||
padding: 8px;
|
||||
background: #ffffe0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title {
|
||||
font-size: 140%;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-content > .para:last-child > p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header" class="notoc">
|
||||
<div id="logo">
|
||||
<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
|
||||
</div>
|
||||
|
||||
<h1 id="site_title"><span>Ruby on Rails</span></h1>
|
||||
<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
|
||||
|
||||
<ul id="navMain">
|
||||
<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
|
||||
<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="container" class="notoc">
|
||||
|
||||
<div id="content">
|
||||
<h1>Ruby on Rails guides</h1>
|
||||
<div id="preamble">
|
||||
<div class="sectionbody">
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/warning.png" alt="Warning" />
|
||||
</td>
|
||||
<td class="content">This page is the result of ongoing <a href="http://hackfest.rubyonrails.org/guide">Rails Guides hackfest</a> and a work in progress.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/caution.png" alt="Caution" />
|
||||
</td>
|
||||
<td class="content">Guides marked with this icon are currently being worked on. While they might still be useful to you, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections at the respective Lighthouse ticket.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<h2>Start Here</h2>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="getting_started_with_rails.html">Getting Started with Rails</a></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/caution.png" alt="Caution" />
|
||||
</td>
|
||||
<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/2">Lighthouse Ticket</a></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>Everything you need to know to install Rails and create your first application.</p></div>
|
||||
</div></div>
|
||||
<h2>Models</h2>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="migrations.html">Rails Database Migrations</a></div>
|
||||
<div class="para"><p>This guide covers how you can use Active Record migrations to alter your database in a structured and organized manner.</p></div>
|
||||
</div></div>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="association_basics.html">Active Record Associations</a></div>
|
||||
<div class="para"><p>This guide covers all the associations provided by Active Record.</p></div>
|
||||
</div></div>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="finders.html">Active Record Finders</a></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/caution.png" alt="Caution" />
|
||||
</td>
|
||||
<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/16">Lighthouse Ticket</a></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>This guide covers the find method defined in ActiveRecord::Base, as well as named scopes.</p></div>
|
||||
</div></div>
|
||||
<h2>Views</h2>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="layouts_and_rendering.html">Layouts and Rendering in Rails</a></div>
|
||||
<div class="para"><p>This guide covers the basic layout features of Action Controller and Action View,
|
||||
including rendering and redirecting, using +content_for_ blocks, and working
|
||||
with partials.</p></div>
|
||||
</div></div>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="form_helpers.html">Action View Form Helpers</a></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/caution.png" alt="Caution" />
|
||||
</td>
|
||||
<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/1">Lighthouse Ticket</a></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>Guide to using built in Form helpers.</p></div>
|
||||
</div></div>
|
||||
<h2>Controllers</h2>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="routing_outside_in.html">Rails Routing from the Outside In</a></div>
|
||||
<div class="para"><p>This guide covers the user-facing features of Rails routing. If you want to
|
||||
understand how to use routing in your own Rails applications, start here.</p></div>
|
||||
</div></div>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="actioncontroller_basics.html">Basics of Action Controller</a></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/caution.png" alt="Caution" />
|
||||
</td>
|
||||
<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/17">Lighthouse Ticket</a></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>This guide covers how controllers work and how they fit into the request cycle in your application. It includes sessions, filters, and cookies, data streaming, and dealing with exceptions raised by a request, among other topics.</p></div>
|
||||
</div></div>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="caching_with_rails.html">Rails Caching</a></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/caution.png" alt="Caution" />
|
||||
</td>
|
||||
<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/10">Lighthouse Ticket</a></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>This guide covers the three types of caching that Rails provides by default.</p></div>
|
||||
</div></div>
|
||||
<h2>Digging Deeper</h2>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="testing_rails_applications.html">Testing Rails Applications</a></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/caution.png" alt="Caution" />
|
||||
</td>
|
||||
<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/8">Lighthouse Ticket</a></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>This is a rather comprehensive guide to doing both unit and functional tests
|
||||
in Rails. It covers everything from “What is a test?” to the testing APIs.
|
||||
Enjoy.</p></div>
|
||||
</div></div>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="security.html">Securing Rails Applications</a></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/caution.png" alt="Caution" />
|
||||
</td>
|
||||
<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/7">Lighthouse Ticket</a></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>This manual describes common security problems in web applications and how to
|
||||
avoid them with Rails.</p></div>
|
||||
</div></div>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="debugging_rails_applications.html">Debugging Rails Applications</a></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/caution.png" alt="Caution" />
|
||||
</td>
|
||||
<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/5">Lighthouse Ticket</a></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>This guide describes how to debug Rails applications. It covers the different
|
||||
ways of achieving this and how to understand what is happening "behind the scenes"
|
||||
of your code.</p></div>
|
||||
</div></div>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="benchmarking_and_profiling.html">Benchmarking and Profiling Rails Applications</a></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/caution.png" alt="Caution" />
|
||||
</td>
|
||||
<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/4">Lighthouse Ticket</a></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>This guide covers ways to analyze and optimize your running Rails code.</p></div>
|
||||
</div></div>
|
||||
<div class="sidebarblock">
|
||||
<div class="sidebar-content">
|
||||
<div class="sidebar-title"><a href="creating_plugins.html">The Basics of Creating Rails Plugins</a></div>
|
||||
<div class="para"><p>This guide covers how to build a plugin to extend the functionality of Rails.</p></div>
|
||||
</div></div>
|
||||
<div class="para"><p>Authors who have contributed to complete guides are listed <a href="authors.html">here</a>.</p></div>
|
||||
<div class="para"><p>This work is licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-Noncommercial-Share Alike 3.0 License</a></p></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,921 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Migrations</title>
|
||||
<!--[if lt IE 8]>
|
||||
<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
|
||||
<![endif]-->
|
||||
<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<style type="text/css">
|
||||
div#container {
|
||||
max-width: 900px;
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
div#content {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
div#container.notoc {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.notoc div#content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
#content p tt {
|
||||
background: #eeeeee;
|
||||
border: solid 1px #cccccc;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#content dt tt {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-left: 3em;
|
||||
}
|
||||
|
||||
#content dt tt, #content pre tt {
|
||||
background: none;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#content .olist ol {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
#header {
|
||||
position: relative;
|
||||
max-width: 840px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#header.notoc {
|
||||
max-width: 580px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
width: 110px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
div#header h1#site_title {
|
||||
background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
|
||||
position: absolute;
|
||||
width: 392px;
|
||||
height: 55px;
|
||||
left: 145px;
|
||||
top: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#site_title span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#site_title_tagline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul#navMain {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: 97px;
|
||||
left: 145px;
|
||||
}
|
||||
|
||||
.left-floaty, .right-floaty {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.admonitionblock,
|
||||
.tableblock {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.admonitionblock .icon {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .content {
|
||||
border: solid 1px #ffda78;
|
||||
background: #fffebd;
|
||||
padding: 10px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.admonitionblock .title {
|
||||
font-size: 140%;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.tableblock table {
|
||||
border: solid 1px #aaaaff;
|
||||
background: #f0f0ff;
|
||||
}
|
||||
|
||||
.tableblock th {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
.tableblock th,
|
||||
.tableblock td {
|
||||
padding: 3px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.sidebarblock {
|
||||
margin-top: 0.25em;
|
||||
margin: 1em;
|
||||
border: solid 1px #ccccbb;
|
||||
padding: 8px;
|
||||
background: #ffffe0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title {
|
||||
font-size: 140%;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-content > .para:last-child > p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebarblock .sidebar-title a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header" >
|
||||
<div id="logo">
|
||||
<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
|
||||
</div>
|
||||
|
||||
<h1 id="site_title"><span>Ruby on Rails</span></h1>
|
||||
<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
|
||||
|
||||
<ul id="navMain">
|
||||
<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
|
||||
<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="sidebar">
|
||||
<h2>Chapters</h2>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="#_anatomy_of_a_migration">Anatomy Of A Migration</a>
|
||||
<ul>
|
||||
|
||||
<li><a href="#_migrations_are_classes">Migrations are classes</a></li>
|
||||
|
||||
<li><a href="#_what_s_in_a_name">What's in a name</a></li>
|
||||
|
||||
<li><a href="#_changing_migrations">Changing migrations</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_creating_a_migration">Creating A Migration</a>
|
||||
<ul>
|
||||
|
||||
<li><a href="#_creating_a_model">Creating a model</a></li>
|
||||
|
||||
<li><a href="#_creating_a_standalone_migration">Creating a standalone migration</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_writing_a_migration">Writing a Migration</a>
|
||||
<ul>
|
||||
|
||||
<li><a href="#_creating_a_table">Creating a table</a></li>
|
||||
|
||||
<li><a href="#_changing_tables">Changing tables</a></li>
|
||||
|
||||
<li><a href="#_special_helpers">Special helpers</a></li>
|
||||
|
||||
<li><a href="#_writing_your_down_method">Writing your down method</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_running_migrations">Running Migrations</a>
|
||||
<ul>
|
||||
|
||||
<li><a href="#_rolling_back">Rolling back</a></li>
|
||||
|
||||
<li><a href="#_being_specific">Being Specific</a></li>
|
||||
|
||||
<li><a href="#_being_talkative">Being talkative</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#models">Using Models In Your Migrations</a>
|
||||
<ul>
|
||||
|
||||
<li><a href="#_dealing_with_changing_models">Dealing with changing models</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_schema_dumping_and_you">Schema dumping and you</a>
|
||||
<ul>
|
||||
|
||||
<li><a href="#schema">What are schema files for?</a></li>
|
||||
|
||||
<li><a href="#_types_of_schema_dumps">Types of schema dumps</a></li>
|
||||
|
||||
<li><a href="#_schema_dumps_and_source_control">Schema dumps and source control</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#foreign_key">Active Record and Referential Integrity</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#_changelog">Changelog</a>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<h1>Migrations</h1>
|
||||
<div id="preamble">
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>Migrations are a convenient way for you to alter your database in a structured and organised manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy. Active Record tracks which migrations have already been run so all you have to do is update your source and run <tt>rake db:migrate</tt>. Active Record will work out which migrations should be run.</p></div>
|
||||
<div class="para"><p>Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of CREATE TABLE any more that you worry about variations on SELECT * (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production.</p></div>
|
||||
<div class="para"><p>You'll learn all about migrations including:</p></div>
|
||||
<div class="ilist"><ul>
|
||||
<li>
|
||||
<p>
|
||||
The generators you can use to create them
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
The methods Active Record provides to manipulate your database
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
The Rake tasks that manipulate them
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
How they relate to <tt>schema.rb</tt>
|
||||
</p>
|
||||
</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 id="_anatomy_of_a_migration">1. Anatomy Of A Migration</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>Before I dive into the details of a migration, here are a few examples of the sorts of things you can do:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreateProducts <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Migration
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
|
||||
create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
|
||||
t<span style="color: #990000">.</span>text <span style="color: #990000">:</span>description
|
||||
|
||||
t<span style="color: #990000">.</span>timestamps
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
|
||||
drop_table <span style="color: #990000">:</span>products
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>This migration adds a table called <tt>products</tt> with a string column called <tt>name</tt> and a text column called <tt>description</tt>. A primary key column called <tt>id</tt> will also be added, however since this is the default we do not need to ask for this. The timestamp columns <tt>created_at</tt> and <tt>updated_at</tt> which Active Record populates automatically will also be added. Reversing this migration is as simple as dropping the table.</p></div>
|
||||
<div class="para"><p>Migrations are not limited to changing the schema. You can also use them to fix bad data in the database or populate new fields:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddReceiveNewsletterToUsers <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Migration
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
|
||||
change_table <span style="color: #990000">:</span>users <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>boolean <span style="color: #990000">:</span>receive_newsletter<span style="color: #990000">,</span> <span style="color: #990000">:</span>default <span style="color: #990000">=></span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
User<span style="color: #990000">.</span>update_all <span style="color: #990000">[</span><span style="color: #FF0000">"receive_newsletter = ?"</span><span style="color: #990000">,</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span><span style="color: #990000">]</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
|
||||
remove_column <span style="color: #990000">:</span>users<span style="color: #990000">,</span> <span style="color: #990000">:</span>receive_newsletter
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>This migration adds an <tt>receive_newsletter</tt> column to the <tt>users</tt> table. We want it to default to <tt>false</tt> for new users, but existing users are considered
|
||||
to have already opted in, so we use the User model to set the flag to <tt>true</tt> for existing users.</p></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/note.png" alt="Note" />
|
||||
</td>
|
||||
<td class="content">Some <a href="#models">caveats</a> apply to using models in your migrations.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<h3 id="_migrations_are_classes">1.1. Migrations are classes</h3>
|
||||
<div class="para"><p>A migration is a subclass of ActiveRecord::Migration that implements two class methods: <tt>up</tt> (perform the required transformations) and <tt>down</tt> (revert them).</p></div>
|
||||
<div class="para"><p>Active Record provides methods that perform common data definition tasks in a database independent way (you'll read about them in detail later):</p></div>
|
||||
<div class="ilist"><ul>
|
||||
<li>
|
||||
<p>
|
||||
<tt>create_table</tt>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<tt>change_table</tt>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<tt>drop_table</tt>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<tt>add_column</tt>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<tt>remove_column</tt>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<tt>change_column</tt>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<tt>rename_column</tt>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<tt>add_index</tt>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<tt>remove_index</tt>
|
||||
</p>
|
||||
</li>
|
||||
</ul></div>
|
||||
<div class="para"><p>If you need to perform tasks specific to your database (for example create a <a href="#foreign_key">foreign key</a> constraint) then the <tt>execute</tt> function allows you to execute arbitrary SQL. A migration is just a regular Ruby class so you're not limited to these functions. For example after adding a column you could
|
||||
write code to set the value of that column for existing records (if necessary using your models).</p></div>
|
||||
<div class="para"><p>On databases that support transactions with statements that change the schema (such as PostgreSQL), migrations are wrapped in a transaction. If the database does not support this (for example MySQL and SQLite) then when a migration fails the parts of it that succeeded will not be rolled back. You will have to unpick the changes that were made by hand.</p></div>
|
||||
<h3 id="_what_s_in_a_name">1.2. What's in a name</h3>
|
||||
<div class="para"><p>Migrations are stored in files in <tt>db/migrate</tt>, one for each migration class. The name of the file is of the form <tt>YYYYMMDDHHMMSS_create_products.rb</tt>, that is to say a UTC timestamp identifying the migration followed by an underscore followed by the name of the migration. The migration class' name must match (the camelcased version of) the latter part of the file name. For example <tt>20080906120000_create_products.rb</tt> should define CreateProducts and <tt>20080906120001_add_details_to_products.rb</tt> should define AddDetailsToProducts. If you do feel the need to change the file name then you MUST update the name of the class inside or Rails will complain about a missing class.</p></div>
|
||||
<div class="para"><p>Internally Rails only uses the migration's number (the timestamp) to identify them. Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated. With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them. With Rails 2.1 this is largely avoided by using the creation time of the migration to identify them. You can revert to the old numbering scheme by setting <tt>config.active_record.timestamped_migrations</tt> to <tt>false</tt> in <tt>environment.rb</tt>.</p></div>
|
||||
<div class="para"><p>The combination of timestamps and recording which migrations have been run allows Rails to handle common situations that occur with multiple developers.</p></div>
|
||||
<div class="para"><p>For example Alice adds migrations <tt>20080906120000</tt> and <tt>20080906123000</tt> and Bob adds <tt>20080906124500</tt> and runs it. Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. Rails knows that it has not run Alice's two migrations so <tt>rake db:migrate</tt> would run them (even though Bob's migration with a later timestamp has been run), and similarly migrating down would not run their down methods.</p></div>
|
||||
<div class="para"><p>Of course this is no substitution for communication within the team, for example if Alice's migration removed a table that Bob's migration assumed the existence of then trouble will still occur.</p></div>
|
||||
<h3 id="_changing_migrations">1.3. Changing migrations</h3>
|
||||
<div class="para"><p>Occasionally you will make a mistake while writing a migration. If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run <tt>rake db:migrate</tt>. You must rollback the migration (for example with <tt>rake db:rollback</tt>), edit your migration and then run <tt>rake db:migrate</tt> to run the corrected version.</p></div>
|
||||
<div class="para"><p>In general editing existing migrations is not a good idea: you will be creating extra work for yourself and your co-workers and cause major headaches if the existing version of the migration has already been run on production machines. Instead you should write a new migration that performs the changes you require. Editing a freshly generated migration that has not yet been committed to source control (or more generally which has not been propagated beyond your development machine) is relatively harmless. Just use some common sense.</p></div>
|
||||
</div>
|
||||
<h2 id="_creating_a_migration">2. Creating A Migration</h2>
|
||||
<div class="sectionbody">
|
||||
<h3 id="_creating_a_model">2.1. Creating a model</h3>
|
||||
<div class="para"><p>The model and scaffold generators will create migrations appropriate for adding a new model. This migration will already contain instructions for creating the relevant table. If you tell Rails what columns you want then statements for adding those will also be created. For example, running</p></div>
|
||||
<div class="para"><p><tt>ruby script/generate model Product name:string description:text</tt> will create a migration that looks like this</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreateProducts <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Migration
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
|
||||
create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
|
||||
t<span style="color: #990000">.</span>text <span style="color: #990000">:</span>description
|
||||
|
||||
t<span style="color: #990000">.</span>timestamps
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
|
||||
drop_table <span style="color: #990000">:</span>products
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>You can append as many column name/type pairs as you want. By default <tt>t.timestamps</tt> (which creates the <tt>updated_at</tt> and <tt>created_at</tt> columns that
|
||||
are automatically populated by Active Record) will be added for you.</p></div>
|
||||
<h3 id="_creating_a_standalone_migration">2.2. Creating a standalone migration</h3>
|
||||
<div class="para"><p>If you are creating migrations for other purposes (for example to add a column to an existing table) then you can use the migration generator:</p></div>
|
||||
<div class="para"><p><tt>ruby script/generate migration AddPartNumberToProducts</tt></p></div>
|
||||
<div class="para"><p>This will create an empty but appropriately named migration:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddPartNumberToProducts <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Migration
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>If the migration name is of the form AddXXXToYYY or RemoveXXXFromY and is followed by a list of column names and types then a migration containing
|
||||
the appropriate add and remove column statements will be created.</p></div>
|
||||
<div class="para"><p><tt>ruby script/generate migration AddPartNumberToProducts part_number:string</tt></p></div>
|
||||
<div class="para"><p>will generate</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddPartNumberToProducts <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Migration
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
|
||||
add_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
|
||||
remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>Similarly,</p></div>
|
||||
<div class="para"><p><tt>ruby script/generate migration RemovePartNumberFromProducts part_number:string</tt></p></div>
|
||||
<div class="para"><p>generates</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> RemovePartNumberFromProducts <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Migration
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
|
||||
remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
|
||||
add_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>You are not limited to one magically generated column, for example</p></div>
|
||||
<div class="para"><p><tt>ruby script/generate migration AddDetailsToProducts part_number:string price:decimal</tt></p></div>
|
||||
<div class="para"><p>generates</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddDetailsToProducts <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Migration
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
|
||||
add_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
|
||||
add_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>price<span style="color: #990000">,</span> <span style="color: #990000">:</span>decimal
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
|
||||
remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>price
|
||||
remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>As always, what has been generated for you is just a starting point. You can add or remove from it as you see fit.</p></div>
|
||||
</div>
|
||||
<h2 id="_writing_a_migration">3. Writing a Migration</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>Once you have created your migration using one of the generators it's time to get to work!</p></div>
|
||||
<h3 id="_creating_a_table">3.1. Creating a table</h3>
|
||||
<div class="para"><p><tt>create_table</tt> will be one of your workhorses. A typical use would be</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>which creates a <tt>products</tt> table with a column called <tt>name</tt> (and as discussed below, an implicit <tt>id</tt> column).</p></div>
|
||||
<div class="para"><p>The object yielded to the block allows you create columns on the table. There are two ways of doing this. The first looks like</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>column <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>string<span style="color: #990000">,</span> <span style="color: #990000">:</span>null <span style="color: #990000">=></span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>the second form, the so called "sexy" migrations, drops the somewhat redundant column method. Instead, the <tt>string</tt>, <tt>integer</tt> etc. methods create a column of that type. Subsequent parameters are identical.</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>null <span style="color: #990000">=></span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>By default <tt>create_table</tt> will create a primary key called <tt>id</tt>. You can change the name of the primary key with the <tt>:primary_key</tt> option (don't forget to update the corresponding model) or if you don't want a primary key at all (for example for a HABTM join table) you can pass <tt>:id ⇒ false</tt>. If you need to pass database specific options you can place an sql fragment in the <tt>:options</tt> option. For example</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>create_table <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>options <span style="color: #990000">=></span> <span style="color: #FF0000">"ENGINE=BLACKHOLE"</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>null <span style="color: #990000">=></span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>Will append <tt>ENGINE=BLACKHOLE</tt> to the sql used to create the table (when using MySQL the default is "ENGINE=InnoDB").</p></div>
|
||||
<div class="para"><p>The types Active Record supports are <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>, <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.</p></div>
|
||||
<div class="para"><p>These will be mapped onto an appropriate underlying database type, for example with MySQL <tt>:string</tt> is mapped to <tt>VARCHAR(255)</tt>. You can create columns of
|
||||
types not supported by Active Record when using the non sexy syntax, for example</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>column <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #FF0000">'polygon'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>null <span style="color: #990000">=></span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>This may however hinder portability to other databases.</p></div>
|
||||
<h3 id="_changing_tables">3.2. Changing tables</h3>
|
||||
<div class="para"><p><tt>create_table</tt>'s close cousin is <tt>change_table</tt>. Used for changing existing tables, it is used in a similar fashion to <tt>create_table</tt> but the object yielded to the block knows more tricks. For example</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>change_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>remove <span style="color: #990000">:</span>description<span style="color: #990000">,</span> <span style="color: #990000">:</span>name
|
||||
t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>part_number
|
||||
t<span style="color: #990000">.</span>index <span style="color: #990000">:</span>part_number
|
||||
t<span style="color: #990000">.</span>rename <span style="color: #990000">:</span>upccode<span style="color: #990000">,</span> <span style="color: #990000">:</span>upc_code
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>removes the <tt>description</tt> column, creates a <tt>part_number</tt> column and adds an index on it. Finally it renames the <tt>upccode</tt> column. This is the same as doing</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>description
|
||||
remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>name
|
||||
add_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
|
||||
add_index <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number
|
||||
rename_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>upccode<span style="color: #990000">,</span> <span style="color: #990000">:</span>upc_code
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>You don't have to keep repeating the table name and it groups all the statements related to modifying one particular table. The individual transformation names are also shorter, for example <tt>remove_column</tt> becomes just <tt>remove</tt> and <tt>add_index</tt> becomes just <tt>index</tt>.</p></div>
|
||||
<h3 id="_special_helpers">3.3. Special helpers</h3>
|
||||
<div class="para"><p>Active Record provides some shortcuts for common functionality. It is for example very common to add both the <tt>created_at</tt> and <tt>updated_at</tt> columns and so there is a method that does exactly that:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>timestamps
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>will create a new products table with those two columns whereas</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>change_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>timestamps
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>adds those columns to an existing table.</p></div>
|
||||
<div class="para"><p>The other helper is called <tt>references</tt> (also available as <tt>belongs_to</tt>). In its simplest form it just adds some readability</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>references <span style="color: #990000">:</span>category
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>will create a <tt>category_id</tt> column of the appropriate type. Note that you pass the model name, not the column name. Active Record adds the <tt>_id</tt> for you. If you have polymorphic belongs_to associations then <tt>references</tt> will add both of the columns required:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>references <span style="color: #990000">:</span>attachment<span style="color: #990000">,</span> <span style="color: #990000">:</span>polymorphic <span style="color: #990000">=></span> <span style="color: #FF0000">{</span><span style="color: #990000">:</span>default <span style="color: #990000">=></span> <span style="color: #FF0000">'Photo'</span><span style="color: #FF0000">}</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>will add an <tt>attachment_id</tt> column and a string <tt>attachment_type</tt> column with a default value of <em>Photo</em>.</p></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/note.png" alt="Note" />
|
||||
</td>
|
||||
<td class="content">The <tt>references</tt> helper does not actually create foreign key constraints for you. You will need to use <tt>execute</tt> for that or a plugin that adds <a href="#foreign_key">foreign key support</a>.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div class="para"><p>If the helpers provided by Active Record aren't enough you can use the <tt>execute</tt> function to execute arbitrary SQL.</p></div>
|
||||
<div class="para"><p>For more details and examples of individual methods check the API documentation, in particular the documentation for <a href="http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html">ActiveRecord::ConnectionAdapters::SchemaStatements</a> (which provides the methods available in the <tt>up</tt> and <tt>down</tt> methods), <a href="http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html">ActiveRecord::ConnectionAdapters::TableDefinition</a> (which provides the methods available on the object yielded by <tt>create_table</tt>) and <a href="http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/Table.html">ActiveRecord::ConnectionAdapters::Table</a> (which provides the methods available on the object yielded by <tt>change_table</tt>).</p></div>
|
||||
<h3 id="_writing_your_down_method">3.4. Writing your down method</h3>
|
||||
<div class="para"><p>The <tt>down</tt> method of your migration should revert the transformations done by the <tt>up</tt> method. In other words the database should be unchanged if you do an <tt>up</tt> followed by a <tt>down</tt>. For example if you create a table in the up you should drop it in the <tt>down</tt> method. It is wise to do things in precisely the reverse order to in the <tt>up</tt> method. For example</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ExampleMigration <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Migration
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
|
||||
create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>references <span style="color: #990000">:</span>category
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-style: italic"><span style="color: #9A1900">#add a foreign key</span></span>
|
||||
execute <span style="color: #FF0000">"ALTER TABLE products ADD CONSTRAINT fk_products_categories FOREIGN KEY (category_id) REFERENCES categories(id)"</span>
|
||||
|
||||
add_column <span style="color: #990000">:</span>users<span style="color: #990000">,</span> <span style="color: #990000">:</span>home_page_url<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
|
||||
|
||||
rename_column <span style="color: #990000">:</span>users<span style="color: #990000">,</span> <span style="color: #990000">:</span>email<span style="color: #990000">,</span> <span style="color: #990000">:</span>email_address
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
|
||||
rename_column <span style="color: #990000">:</span>users<span style="color: #990000">,</span> <span style="color: #990000">:</span>email_address<span style="color: #990000">,</span> <span style="color: #990000">:</span>email
|
||||
remove_column <span style="color: #990000">:</span>users<span style="color: #990000">,</span> <span style="color: #990000">:</span>home_page_url
|
||||
execute <span style="color: #FF0000">"ALTER TABLE products DROP FOREIGN KEY fk_products_categories"</span>
|
||||
drop_table <span style="color: #990000">:</span>products
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>Sometimes your migration will do something which is just plain irreversible, for example it might destroy some data. In cases like those when you can't reverse the migration you can raise IrreversibleMigration from your <tt>down</tt> method. If someone tries to revert your migration an error message will be
|
||||
displayed saying that it can't be done.</p></div>
|
||||
</div>
|
||||
<h2 id="_running_migrations">4. Running Migrations</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>Rails provides a set of rake tasks to work with migrations which boils down to running certain sets of migrations. The very first migration related rake task you use will probably be <tt>db:migrate</tt>. In its most basic form it just runs the <tt>up</tt> method for all the migrations that have not yet been run. If there are no such migrations it exits.</p></div>
|
||||
<div class="para"><p>If you specify a target version, Active Record will run the required migrations (up or down) until it has reached the specified version. The
|
||||
version is the numerical prefix on the migration's filename. For example to migrate to version 20080906120000 run</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt>rake db:migrate VERSION=20080906120000</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>If this is greater than the current version (i.e. it is migrating upwards) this will run the <tt>up</tt> method on all migrations up to and including 20080906120000, if migrating downwards this will run the <tt>down</tt> method on all the migrations down to, but not including, 20080906120000.</p></div>
|
||||
<h3 id="_rolling_back">4.1. Rolling back</h3>
|
||||
<div class="para"><p>A common task is to rollback the last migration, for example if you made a mistake in it and wish to correct it. Rather than tracking down the version number associated with the previous migration you can run</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt>rake db:rollback</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>This will run the <tt>down</tt> method from the latest migration. If you need to undo several migrations you can provide a <tt>STEP</tt> parameter:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt>rake db:rollback STEP=3</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>will run the <tt>down</tt> method fron the last 3 migrations.</p></div>
|
||||
<div class="para"><p>The <tt>db:migrate:redo</tt> task is a shortcut for doing a rollback and then migrating back up again. As with the <tt>db:rollback</tt> task you can use the <tt>STEP</tt> parameter if you need to go more than one version back, for example</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt>rake db:migrate:redo STEP=3</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>Neither of these Rake tasks do anything you could not do with <tt>db:migrate</tt>, they are simply more convenient since you do not need to explicitly specify the version to migrate to.</p></div>
|
||||
<div class="para"><p>Lastly, the <tt>db:reset</tt> task will drop the database, recreate it and load the current schema into it.</p></div>
|
||||
<div class="admonitionblock">
|
||||
<table><tr>
|
||||
<td class="icon">
|
||||
<img src="/Users/lifo/Docs/docrails/railties/doc/guides/source/icons/note.png" alt="Note" />
|
||||
</td>
|
||||
<td class="content">This is not the same as running all the migrations - see the section on <a href="#schema">schema.rb</a>.</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<h3 id="_being_specific">4.2. Being Specific</h3>
|
||||
<div class="para"><p>If you need to run a specific migration up or down the <tt>db:migrate:up</tt> and <tt>db:migrate:down</tt> tasks will do that. Just specify the appropriate version and the corresponding migration will have its <tt>up</tt> or <tt>down</tt> method invoked, for example</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt>rake db:migrate:up VERSION=20080906120000</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>will run the <tt>up</tt> method from the 20080906120000 migration. These tasks check whether the migration has already run, so for example <tt>db:migrate:up VERSION=20080906120000</tt> will do nothing if Active Record believes that 20080906120000 has already been run.</p></div>
|
||||
<h3 id="_being_talkative">4.3. Being talkative</h3>
|
||||
<div class="para"><p>By default migrations tell you exactly what they're doing and how long it took.
|
||||
A migration creating a table and adding an index might produce output like this</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt>== 20080906170109 CreateProducts: migrating ===================================
|
||||
-- create_table(:products)
|
||||
-> 0.0021s
|
||||
-- add_index(:products, :name)
|
||||
-> 0.0026s
|
||||
== 20080906170109 CreateProducts: migrated (0.0059s) ==========================</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>Several methods are provided that allow you to control all this:</p></div>
|
||||
<div class="ilist"><ul>
|
||||
<li>
|
||||
<p>
|
||||
<tt>suppress_messages</tt> suppresses any output generated by its block
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<tt>say</tt> outputs text (the second argument controls whether it is indented or not)
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<tt>say_with_time</tt> outputs text along with how long it took to run its block. If the block returns an integer it assumes it is the number of rows affected.
|
||||
</p>
|
||||
</li>
|
||||
</ul></div>
|
||||
<div class="para"><p>For example, this migration</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreateProducts <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Migration
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
|
||||
suppress_messages <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
|
||||
create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
|
||||
t<span style="color: #990000">.</span>text <span style="color: #990000">:</span>description
|
||||
t<span style="color: #990000">.</span>timestamps
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
say <span style="color: #FF0000">"Created a table"</span>
|
||||
suppress_messages <span style="color: #FF0000">{</span>add_index <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>name<span style="color: #FF0000">}</span>
|
||||
say <span style="color: #FF0000">"and an index!"</span><span style="color: #990000">,</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
|
||||
say_with_time <span style="color: #FF0000">'Waiting for a while'</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
|
||||
sleep <span style="color: #993399">10</span>
|
||||
<span style="color: #993399">250</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
|
||||
drop_table <span style="color: #990000">:</span>products
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>generates the following output</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre><tt>== 20080906170109 CreateProducts: migrating ===================================
|
||||
-- Created a table
|
||||
-> and an index!
|
||||
-- Waiting for a while
|
||||
-> 10.0001s
|
||||
-> 250 rows
|
||||
== 20080906170109 CreateProducts: migrated (10.0097s) =========================</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>If you just want Active Record to shut up then running <tt>rake db:migrate VERBOSE=false</tt> will suppress any output.</p></div>
|
||||
</div>
|
||||
<h2 id="models">5. Using Models In Your Migrations</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>When creating or updating data in a migration it is often tempting to use one of your models. After all they exist to provide easy access to the underlying data. This can be done but some caution should be observed.</p></div>
|
||||
<div class="para"><p>Consider for example a migration that uses the Product model to update a row in the corresponding table. Alice later updates the Product model, adding a new column and a validation on it. Bob comes back from holiday, updates the source and runs outstanding migrations with <tt>rake db:migrate</tt>, including the one that used the Product model. When the migration runs the source is up to date and so the Product model has the validation added by Alice. The database however is still old and so does not have that column and an error ensues because that validation is on a column that does not yet exist.</p></div>
|
||||
<div class="para"><p>Frequently I just want to update rows in the database without writing out the SQL by hand: I'm not using anything specific to the model. One pattern for this is to define a copy of the model inside the migration itself, for example:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddPartNumberToProducts <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Migration
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Product <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
|
||||
<span style="color: #990000">...</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
|
||||
<span style="color: #990000">...</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>The migration has its own minimal copy of the Product model and no longer cares about the Product model defined in the application.</p></div>
|
||||
<h3 id="_dealing_with_changing_models">5.1. Dealing with changing models</h3>
|
||||
<div class="para"><p>For performance reasons information about the columns a model has is cached. For example if you add a column to a table and then try and use the corresponding model to insert a new row it may try and use the old column information. You can force Active Record to re-read the column information with the <tt>reset_column_information</tt> method, for example</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddPartNumberToProducts <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Migration
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Product <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
|
||||
add_column <span style="color: #990000">:</span>product<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
|
||||
Product<span style="color: #990000">.</span>reset_column_information
|
||||
<span style="color: #990000">...</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
|
||||
<span style="color: #990000">...</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
</div>
|
||||
<h2 id="_schema_dumping_and_you">6. Schema dumping and you</h2>
|
||||
<div class="sectionbody">
|
||||
<h3 id="schema">6.1. What are schema files for?</h3>
|
||||
<div class="para"><p>Migrations, mighty as they may be, are not the authoritative source for your database schema. That role falls to either <tt>schema.rb</tt> or an SQL file which Active Record generates by examining the database. They are not designed to be edited, they just represent the current state of the database.</p></div>
|
||||
<div class="para"><p>There is no need (and it is error prone) to deploy a new instance of an app by replaying the entire migration history. It is much simpler and faster to just load into the database a description of the current schema.</p></div>
|
||||
<div class="para"><p>For example, this is how the test database is created: the current development database is dumped (either to <tt>schema.rb</tt> or <tt>development.sql</tt>) and then loaded into the test database.</p></div>
|
||||
<div class="para"><p>Schema files are also useful if want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations but is all summed up in the schema file. The <a href="http://agilewebdevelopment.com/plugins/annotate_models">annotate_models</a> plugin, which automatically adds (and updates) comments at the top of each model summarising the schema, may also be of interest.</p></div>
|
||||
<h3 id="_types_of_schema_dumps">6.2. Types of schema dumps</h3>
|
||||
<div class="para"><p>There are two ways to dump the schema. This is set in <tt>config/environment.rb</tt> by the <tt>config.active_record.schema_format</tt> setting, which may be either <tt>:sql</tt> or <tt>:ruby</tt>.</p></div>
|
||||
<div class="para"><p>If <tt>:ruby</tt> is selected then the schema is stored in <tt>db/schema.rb</tt>. If you look at this file you'll find that it looks an awful lot like one very big migration:</p></div>
|
||||
<div class="listingblock">
|
||||
<div class="content"><!-- Generator: GNU source-highlight 2.9
|
||||
by Lorenzo Bettini
|
||||
http://www.lorenzobettini.it
|
||||
http://www.gnu.org/software/src-highlite -->
|
||||
<pre><tt>ActiveRecord<span style="color: #990000">::</span>Schema<span style="color: #990000">.</span>define<span style="color: #990000">(:</span>version <span style="color: #990000">=></span> <span style="color: #993399">20080906171750</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
|
||||
create_table <span style="color: #FF0000">"authors"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>force <span style="color: #990000">=></span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>string <span style="color: #FF0000">"name"</span>
|
||||
t<span style="color: #990000">.</span>datetime <span style="color: #FF0000">"created_at"</span>
|
||||
t<span style="color: #990000">.</span>datetime <span style="color: #FF0000">"updated_at"</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
|
||||
create_table <span style="color: #FF0000">"products"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>force <span style="color: #990000">=></span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
|
||||
t<span style="color: #990000">.</span>string <span style="color: #FF0000">"name"</span>
|
||||
t<span style="color: #990000">.</span>text <span style="color: #FF0000">"description"</span>
|
||||
t<span style="color: #990000">.</span>datetime <span style="color: #FF0000">"created_at"</span>
|
||||
t<span style="color: #990000">.</span>datetime <span style="color: #FF0000">"updated_at"</span>
|
||||
t<span style="color: #990000">.</span>string <span style="color: #FF0000">"part_number"</span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
|
||||
</tt></pre></div></div>
|
||||
<div class="para"><p>In many ways this is exactly what it is. This file is created by inspecting the database and expressing its structure using <tt>create_table</tt>, <tt>add_index</tt> and so on. Because this is database independent it could be loaded into any database that Active Record supports. This could be very useful if you were to distribute an application that is able to run against multiple databases.</p></div>
|
||||
<div class="para"><p>There is however a trade-off: <tt>schema.rb</tt> cannot express database specific items such as foreign key constraints, triggers or stored procedures. While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this then you should set the schema format to <tt>:sql</tt>.</p></div>
|
||||
<div class="para"><p>Instead of using Active Record's schema dumper the database's structure will be dumped using a tool specific to that database (via the <tt>db:structure:dump</tt> Rake task) into <tt>db/#{RAILS_ENV}_structure.sql</tt>. For example for PostgreSQL the <tt>pg_dump</tt> utility is used and for MySQL this file will contain the output of SHOW CREATE TABLE for the various tables. Loading this schema is simply a question of executing the SQL statements contained inside.</p></div>
|
||||
<div class="para"><p>By definition this will be a perfect copy of the database's structure but this will usually prevent loading the schema into a database other than the one used to create it.</p></div>
|
||||
<h3 id="_schema_dumps_and_source_control">6.3. Schema dumps and source control</h3>
|
||||
<div class="para"><p>Because they are the authoritative source for your database schema, it is strongly recommended that you check them into source control.</p></div>
|
||||
</div>
|
||||
<h2 id="foreign_key">7. Active Record and Referential Integrity</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>The Active Record way is that intelligence belongs in your models, not in the database. As such features such as triggers or foreign key constraints, which push some of that intelligence back into the database are not heavily used.</p></div>
|
||||
<div class="para"><p>Validations such as <tt>validates_uniqueness_of</tt> are one way in which models can enforce data integrity. The <tt>:dependent</tt> option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints.</p></div>
|
||||
<div class="para"><p>Although Active Record does not provide any tools for working directly with such features, the <tt>execute</tt> method can be used to execute arbitrary SQL. There are also a number of plugins such as <a href="http://agilewebdevelopment.com/plugins/search?search=redhillonrails">redhillonrails</a> which add foreign key support to Active Record (including support for dumping foreign keys in <tt>schema.rb</tt>).</p></div>
|
||||
</div>
|
||||
<h2 id="_changelog">8. Changelog</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/6">Lighthouse ticket</a></p></div>
|
||||
<div class="ilist"><ul>
|
||||
<li>
|
||||
<p>
|
||||
September 14, 2008: initial version by <a href="../authors.html#fcheung">Frederick Cheung</a>
|
||||
</p>
|
||||
</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,120 @@
|
|||
ActiveRecord Basics
|
||||
=================================
|
||||
This guide will explain in detail how the ActiveRecord design pattern is used inside Ruby on Rails to make communication with the database clear and easy to understand.
|
||||
The intent of this guide is to explain the ActiveRecord implementation used by Rails though easy to understand examples, metaphors and detailed explanations of the actual Rails source code.
|
||||
After reading this guide readers should have a strong grasp of the ActiveRecord concept and how it can be used with or without Rails. Hopefully, some of the philosophical and theoretical intentions discussed here will also make them a stronger and better developer.
|
||||
== ORM The Blueprint of ActiveRecord
|
||||
If ActiveRecord is the engine of Rails then ORM is the blueprint of that engine. ORM is short for “Object Relational Mapping” and is a programming concept used to make structures within a system relational. ORM seeks to give semantic meaning to the associations between elements of the system for example tables within a database.
|
||||
As a thought experiment imagine the components that make up a typical car. There are doors, seats, windows, engines etc. Viewed independently they are simple parts, yet when bolted together through the aid of a blueprint, the parts become a more complex device. ORM is the blueprint that describes how the individual parts relate to one another and in some cases infers the part’s purpose through the way the associations are described.
|
||||
== ActiveRecord The Engine of Rails
|
||||
ActiveRecord is a metaphor used to access data within a database. The name “Active Record” was coined by Martin Fowler in his book “Patterns of Enterprise Application Architecture”. ActiveRecord is a conceptual model of the database record and the relationships to other records.
|
||||
As a side note, from now when I refer to ActiveRecord I’ll be referring to the specific Rails implementation and not the design pattern in general. I make this distinction because, as Rails has evolved so too has the Rails specific implementation of their version of ActiveRecord.
|
||||
Specifically, the Rails ActiveRecord pattern adds inheritance and associations. The associations are created by using a DSL (domain specific language) of macros, and a STI (Single Table Inheritance) to facilitate the inheritance.
|
||||
Rails uses ActiveRecord to abstract much of the drudgery or C.R.U.D (explained later) of working with data in databases. Using ActiveRecord Rails automates the mapping between:
|
||||
* Classes & Database Tables
|
||||
* Class attributes & Database Table Columns
|
||||
For example suppose you created a database table called cars:
|
||||
[source, sql]
|
||||
-------------------------------------------------------
|
||||
mysql> CREATE TABLE cars (
|
||||
id INT,
|
||||
color VARCHAR(100),
|
||||
doors INT,
|
||||
horses INT,
|
||||
model VARCHAR(100)
|
||||
);
|
||||
-------------------------------------------------------
|
||||
Now you created a class named Car, which is to represent an instance of a record from your table.
|
||||
[source, ruby]
|
||||
-------------------------------------------------------
|
||||
class Car
|
||||
end
|
||||
-------------------------------------------------------
|
||||
As you might expect without defining the explicit mappings between your class and the table it is impossible for Rails or any other program to correctly map those relationships.
|
||||
[source, ruby]
|
||||
-------------------------------------------------------
|
||||
>> c = Car.new
|
||||
=> #<Class:0x11e1e90>
|
||||
>> c.doors
|
||||
NoMethodError: undefined method `doors' for #<Class:0x11e1e90>
|
||||
from (irb):2
|
||||
-------------------------------------------------------
|
||||
Now you could define a door methods to write and read data to and from the database. In a nutshell this is what ActiveRecord does. According to the Rails API:
|
||||
“Active Record objects don‘t specify their attributes directly, but rather infer them from the table definition with which they‘re linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.”
|
||||
Lets try our Car class again, this time inheriting from ActiveRecord.
|
||||
[source, ruby]
|
||||
-------------------------------------------------------
|
||||
class Car < ActiveRecord::Base
|
||||
end
|
||||
-------------------------------------------------------
|
||||
Now if we try to access an attribute of the table ActiveRecord automatically handles the mappings for us, as you can see in the following example.
|
||||
[source, ruby]
|
||||
-------------------------------------------------------
|
||||
>> c = Car.new
|
||||
=> #<Car id: nil, doors: nil, color: nil, horses: nil, model: nil>
|
||||
>> c.doors
|
||||
=> nil
|
||||
-------------------------------------------------------
|
||||
|
||||
This wrapper implements attribute accessors, callbacks and validations, which can make the data more powerful.
|
||||
- Validations
|
||||
* create!
|
||||
* validates_acceptance_of
|
||||
* validates_associated
|
||||
* validates_confirmation_of
|
||||
* validates_each
|
||||
* validates_exclusion_of
|
||||
* validates_format_of
|
||||
* validates_inclusion_of
|
||||
* validates_length_of
|
||||
* validates_numericality_of
|
||||
* validates_presence_of
|
||||
* validates_size_of
|
||||
* validates_uniqueness_of
|
||||
- Callback
|
||||
* (-) save
|
||||
* (-) valid
|
||||
* (1) before_validation
|
||||
* (2) before_validation_on_create
|
||||
* (-) validate
|
||||
* (-) validate_on_create
|
||||
* (3) after_validation
|
||||
* (4) after_validation_on_create
|
||||
* (5) before_save
|
||||
* (6) before_create
|
||||
* (-) create
|
||||
* (7) after_create
|
||||
* (8) after_save
|
||||
|
||||
Rails further extends this model by giving each ActiveRecord a way of describing the variety of ways records are associated with one another. We will touch on some of these associations later in the guide but I encourage readers who are interested to read the guide to ActiveRecord associations for an in-depth explanation of the variety of ways rails can model associations.
|
||||
- Associations between objects controlled by meta-programming macros.
|
||||
|
||||
== Philosophical Approaches & Common Conventions
|
||||
Rails has a reputation of being a zero-config framework which means that it aims to get you off the ground with as little pre-flight checking as possible. This speed benefit is achieved by following “Convention over Configuration”, which is to say that if you agree to live with the defaults then you benefit from a the inherent speed-boost. As Courtneay Gasking put it to me once “You don’t want to off-road on Rails”. ActiveRecord is no different, while it’s possible to override or subvert any of the conventions of AR, unless you have a good reason for doing so you will probably be happy with the defaults. The following is a list of the common conventions of ActiveRecord
|
||||
|
||||
|
||||
ActiveRecord is the default model component of the Model-view-controller web-application framework Ruby on Rails, and is also a stand-alone ORM package for other Ruby applications. In both forms, it was conceived of by David Heinemeier Hansson, and has been improved upon by a number of contributors. --wikipedia
|
||||
|
||||
- Naming Conventions
|
||||
- Class Names are Singular
|
||||
- Tables names are the plural name of the class name
|
||||
- Tables contain an identity column named id
|
||||
- ids
|
||||
== ActiveRecord Magic
|
||||
- timestamps
|
||||
- updates
|
||||
|
||||
== How ActiveRecord Maps your Database.
|
||||
- sensible defaults
|
||||
- overriding conventions
|
||||
|
||||
== Growing Your Database Relationships Naturally
|
||||
|
||||
== Attributes
|
||||
- attribute accessor method. How to override them?
|
||||
- attribute?
|
||||
- dirty records
|
||||
-
|
||||
== ActiveRecord handling the CRUD of your Rails application - Understanding the life-cycle of an ActiveRecord
|
||||
|
||||
== Validations & Callbacks
|
|
@ -1085,7 +1085,8 @@ The +_collection_.delete+ method removes one or more objects from the collection
|
|||
@customer.orders.delete(@order1)
|
||||
-------------------------------------------------------
|
||||
|
||||
WARNING: The +_collection_.delete+ method will destroy the deleted object if they are declared as +belongs_to+ and are dependent on this model.
|
||||
WARNING: Objects will be in addition destroyed if they're associated with +:dependent => :destroy+, and deleted if they're associated with +:dependent => :delete_all+.
|
||||
|
||||
|
||||
===== +_collection_=objects+
|
||||
|
|
@ -90,15 +90,6 @@ service both for when in development and when you put your application into prod
|
|||
|
||||
#TODO more in-depth without being like an advertisement.
|
||||
|
||||
=== FiveRuns ===
|
||||
http://www.fiveruns.com/[http://www.fiveruns.com/]
|
||||
|
||||
#TODO give a bit more detail
|
||||
|
||||
==== TuneUp ====
|
||||
|
||||
In their words "a new socially networked application profiling tool for Ruby on Rails developers. Designed for rapid application performance analysis in development, both privately or collaboratively with input from the community, FiveRuns TuneUp gives developers visibility into performance trouble spots and bottlenecks before they reach production."
|
||||
|
||||
==== Manage ====
|
||||
|
||||
Like new relic a production monitoring tool.
|
|
@ -0,0 +1,241 @@
|
|||
Benchmarking and Profiling Rails
|
||||
================================
|
||||
|
||||
This guide covers the benchmarking and profiling tactics/tools of Rails and Ruby in general. By referring to this guide, you will be able to:
|
||||
|
||||
* Understand the various types of benchmarking and profiling metrics
|
||||
* Generate performance/benchmarking tests
|
||||
* Use GC patched Ruby binary to measure memory usage and object allocation
|
||||
* Understand the information provided by Rails inside the log files
|
||||
* Learn about various tools facilitating benchmarking and profiling
|
||||
|
||||
== Why Benchmark and Profile ?
|
||||
|
||||
Benchmarking and Profiling is an integral part of the development cycle. It is very important that you don't make your end users wait for too long before the page is completely loaded. Ensuring a plesant browsing experience to the end users and cutting cost of unnecessary hardwares is important for any web application.
|
||||
|
||||
=== What is the difference between benchmarking and profiling ? ===
|
||||
|
||||
Benchmarking is the process of finding out if a piece of code is slow or not. Whereas profiling is the process of finding out what exactly is slowing down that piece of code.
|
||||
|
||||
== Using and understanding the log files ==
|
||||
|
||||
Rails logs files containt basic but very useful information about the time taken to serve every request. A typical log entry looks something like :
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
Processing ItemsController#index (for 127.0.0.1 at 2008-10-17 00:08:18) [GET]
|
||||
Session ID: BAh7BiIKZmxhc2hJQzonQWN0aHsABjoKQHVzZWR7AA==--83cff4fe0a897074a65335
|
||||
Parameters: {"action"=>"index", "controller"=>"items"}
|
||||
Rendering template within layouts/items
|
||||
Rendering items/index
|
||||
Completed in 5ms (View: 2, DB: 0) | 200 OK [http://localhost/items]
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
For this section, we're only interested in the last line from that log entry:
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
Completed in 5ms (View: 2, DB: 0) | 200 OK [http://localhost/items]
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
This data is fairly straight forward to understand. Rails uses millisecond(ms) as the metric to measures the time taken. The complete request spent 5 ms inside Rails, out of which 2 ms were spent rendering views and none was spent communication with the database. It's safe to assume that the remaining 3 ms were spent inside the controller.
|
||||
|
||||
== Helper methods ==
|
||||
|
||||
Rails provides various helper methods inside Active Record, Action Controller and Action View to measure the time taken by a specific code. The method is called +benchmark()+ in all three components.
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
Project.benchmark("Creating project") do
|
||||
project = Project.create("name" => "stuff")
|
||||
project.create_manager("name" => "David")
|
||||
project.milestones << Milestone.find(:all)
|
||||
end
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
The above code benchmarks the multiple statments enclosed inside +Project.benchmark("Creating project") do..end+ block and prints the results inside log files. The statement inside log files will look like:
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
Creating projectem (185.3ms)
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Please refer to http://api.rubyonrails.com/classes/ActiveRecord/Base.html#M001336[API docs] for optional options to +benchmark()+
|
||||
|
||||
Similarly, you could use this helper method inside http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715[controllers] ( Note that it's a class method here ):
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
def process_projects
|
||||
self.class.benchmark("Processing projects") do
|
||||
Project.process(params[:project_ids])
|
||||
Project.update_cached_projects
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
and http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715[views]:
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
<% benchmark("Showing projects partial") do %>
|
||||
<%= render :partial => @projects %>
|
||||
<% end %>
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
== Performance Test Cases ==
|
||||
|
||||
Rails provides a very easy to write performance test cases, which look just like the regular integration tests.
|
||||
|
||||
If you have a look at +test/performance/browsing_test.rb+ in a newly created Rails application:
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
require 'test_helper'
|
||||
require 'performance_test_help'
|
||||
|
||||
# Profiling results for each test method are written to tmp/performance.
|
||||
class BrowsingTest < ActionController::PerformanceTest
|
||||
def test_homepage
|
||||
get '/'
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
This is an automatically generated example performance test file, for testing performance of homepage('/') of the application.
|
||||
|
||||
=== Modes ===
|
||||
|
||||
==== Benchmarking ====
|
||||
==== Profiling ====
|
||||
|
||||
=== Metrics ===
|
||||
|
||||
==== Process Time ====
|
||||
|
||||
CPU Cycles.
|
||||
|
||||
==== Memory ====
|
||||
|
||||
Memory taken.
|
||||
|
||||
==== Objects ====
|
||||
|
||||
Objects allocated.
|
||||
|
||||
==== GC Runs ====
|
||||
|
||||
Number of times the Ruby GC was run.
|
||||
|
||||
==== GC Time ====
|
||||
|
||||
Time spent running the Ruby GC.
|
||||
|
||||
=== Preparing Ruby and Ruby-prof ===
|
||||
|
||||
Before we go ahead, Rails performance testing requires you to build a special Ruby binary with some super powers - GC patch for measuring GC Runs/Time. This process is very straight forward. If you've never compiled a Ruby binary before, you can follow the following steps to build a ruby binary inside your home directory:
|
||||
|
||||
==== Compile ====
|
||||
|
||||
[source, shell]
|
||||
----------------------------------------------------------------------------
|
||||
[lifo@null ~]$ mkdir rubygc
|
||||
[lifo@null ~]$ wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p111.tar.gz
|
||||
[lifo@null ~]$ tar -xzvf ruby-1.8.6-p111.tar.gz
|
||||
[lifo@null ~]$ cd ruby-1.8.6-p111
|
||||
[lifo@null ruby-1.8.6-p111]$ curl http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch | patch -p0
|
||||
[lifo@null ruby-1.8.6-p111]$ ./configure --prefix=/Users/lifo/rubygc
|
||||
[lifo@null ruby-1.8.6-p111]$ make && make install
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
==== Prepare aliases ====
|
||||
|
||||
Add the following lines in your ~/.profile for convenience:
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
alias gcruby='/Users/lifo/rubygc/bin/ruby'
|
||||
alias gcrake='/Users/lifo/rubygc/bin/rake'
|
||||
alias gcgem='/Users/lifo/rubygc/bin/gem'
|
||||
alias gcirb='/Users/lifo/rubygc/bin/irb'
|
||||
alias gcrails='/Users/lifo/rubygc/bin/rails'
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
==== Install rubygems and some basic gems ====
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
[lifo@null ~]$ wget http://rubyforge.org/frs/download.php/38646/rubygems-1.2.0.tgz
|
||||
[lifo@null ~]$ tar -xzvf rubygems-1.2.0.tgz
|
||||
[lifo@null ~]$ cd rubygems-1.2.0
|
||||
[lifo@null rubygems-1.2.0]$ gcruby setup.rb
|
||||
[lifo@null rubygems-1.2.0]$ cd ~
|
||||
[lifo@null ~]$ gcgem install rake
|
||||
[lifo@null ~]$ gcgem install rails
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
==== Install MySQL gem ====
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
[lifo@null ~]$ gcgem install mysql
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
If this fails, you can try to install it manually:
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
[lifo@null ~]$ cd /Users/lifo/rubygc/lib/ruby/gems/1.8/gems/mysql-2.7/
|
||||
[lifo@null mysql-2.7]$ gcruby extconf.rb --with-mysql-config
|
||||
[lifo@null mysql-2.7]$ make && make install
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
=== Installing Jeremy Kemper's ruby-prof ===
|
||||
|
||||
We also need to install Jeremy's ruby-prof gem using our newly built ruby:
|
||||
|
||||
[source, shell]
|
||||
----------------------------------------------------------------------------
|
||||
[lifo@null ~]$ git clone git://github.com/jeremy/ruby-prof.git
|
||||
[lifo@null ~]$ cd ruby-prof/
|
||||
[lifo@null ruby-prof (master)]$ gcrake gem
|
||||
[lifo@null ruby-prof (master)]$ gcgem install pkg/ruby-prof-0.6.1.gem
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
=== Generating performance test ===
|
||||
|
||||
Rails provides a simple generator for creating new performance tests:
|
||||
|
||||
[source, shell]
|
||||
----------------------------------------------------------------------------
|
||||
[User profiling_tester (master)]$ script/generate performance_test homepage
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
This will generate +test/performance/homepage_test.rb+:
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
require 'test_helper'
|
||||
require 'performance_test_help'
|
||||
|
||||
class HomepageTest < ActionController::PerformanceTest
|
||||
# Replace this with your real tests.
|
||||
def test_homepage
|
||||
get '/'
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Which you can modify to suit your needs.
|
||||
|
||||
=== Running tests ===
|
||||
|
||||
include::rubyprof.txt[]
|
||||
|
||||
include::digging_deeper.txt[]
|
||||
|
||||
include::gameplan.txt[]
|
||||
|
||||
include::appendix.txt[]
|
||||
|
||||
== Changelog ==
|
||||
|
||||
http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/4[Lighthouse ticket]
|
||||
|
||||
* October 17, 2008: First revision by Pratik
|
||||
* September 6, 2008: Initial version by Matthew Bergman <MzbPhoto@gmail.com>
|
|
@ -1,7 +1,7 @@
|
|||
Debugging Rails Applications
|
||||
============================
|
||||
|
||||
This guide covers how to debug Ruby on Rails applications. By referring to this guide, you will be able to:
|
||||
This guide introduces techniques for debugging Ruby on Rails applications. By referring to this guide, you will be able to:
|
||||
|
||||
* Understand the purpose of debugging
|
||||
* Track down problems and issues in your application that your tests aren't identifying
|
||||
|
@ -18,7 +18,7 @@ One common task is to inspect the contents of a variable. In Rails, you can do t
|
|||
|
||||
=== debug
|
||||
|
||||
`debug` will return a <pre>-tag that has object dumped by YAML. Generating readable output to inspect any object.
|
||||
The `debug` helper will return a <pre>-tag that renders the object using the YAML format. This will generate human-readable data from any object. For example, if you have this code in a view:
|
||||
|
||||
[source, html]
|
||||
----------------------------------------------------------------------------
|
||||
|
@ -29,8 +29,9 @@ One common task is to inspect the contents of a variable. In Rails, you can do t
|
|||
</p>
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Will render something like this:
|
||||
You'll see something like this:
|
||||
|
||||
[source, log]
|
||||
----------------------------------------------------------------------------
|
||||
--- !ruby/object:Post
|
||||
attributes:
|
||||
|
@ -59,10 +60,11 @@ Displaying an instance variable, or any other object or method, in yaml format c
|
|||
</p>
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
`to_yaml` converts the method to yaml format leaving it more readable and finally `simple_format` help us to render each line as in the console. This is how `debug` method does its magic.
|
||||
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:
|
||||
|
||||
[source, log]
|
||||
----------------------------------------------------------------------------
|
||||
--- !ruby/object:Post
|
||||
attributes:
|
||||
|
@ -79,7 +81,7 @@ Title: Rails debugging guide
|
|||
|
||||
=== inspect
|
||||
|
||||
Another useful method for displaying object values is `inspect`, especially when working with arrays or hashes, it will print the object value as a string, for example:
|
||||
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:
|
||||
|
||||
[source, html]
|
||||
----------------------------------------------------------------------------
|
||||
|
@ -92,6 +94,7 @@ Another useful method for displaying object values is `inspect`, especially when
|
|||
|
||||
Will be rendered as follows:
|
||||
|
||||
[source, log]
|
||||
----------------------------------------------------------------------------
|
||||
[1, 2, 3, 4, 5]
|
||||
|
||||
|
@ -100,11 +103,13 @@ Title: Rails debugging guide
|
|||
|
||||
== The Logger
|
||||
|
||||
=== What is it?
|
||||
It can also be useful to save information to log files at runtime. Rails maintains a separate log file for each runtime environment.
|
||||
|
||||
Rails makes use of ruby's standard `logger` to write log information. You can also substitute another logger such as `Log4R` if you wish.
|
||||
=== What is The Logger?
|
||||
|
||||
If you want to change the logger you can specify it in your +environment.rb+ or any environment file.
|
||||
Rails makes use of Ruby's standard `logger` to write log information. You can also substitute another logger such as `Log4R` if you wish.
|
||||
|
||||
You can specify an alternative logger in your +environment.rb+ or any environment file:
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
|
@ -112,7 +117,7 @@ ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|||
ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Or in the `__Initializer__` section, add _any_ of the following
|
||||
Or in the +Initializer+ section, add _any_ of the following
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
|
@ -125,9 +130,9 @@ By default, each log is created under `RAILS_ROOT/log/` and the log file name is
|
|||
|
||||
=== Log Levels
|
||||
|
||||
When something is logged it's printed into the corresponding log if the message log level is equal or higher than the configured log level. If you want to know the current log level just call `ActiveRecord::Base.logger.level` method.
|
||||
When something is logged it's printed into the corresponding log if the log level of the message is equal or higher than the configured log level. If you want to know the current log level you can call the `ActiveRecord::Base.logger.level` method.
|
||||
|
||||
The available log levels are: +:debug+, +:info+, +:warn+, +:error+, +:fatal+, each level has a log level number from 0 up to 4 respectively. To change the default log level, use
|
||||
The available log levels are: +:debug+, +:info+, +:warn+, +:error+, and +:fatal+, corresponding to the log level numbers from 0 up to 4 respectively. To change the default log level, use
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
|
@ -138,7 +143,7 @@ ActiveRecord::Base.logger.level = 0 # at any time
|
|||
This is useful when you want to log under development or staging, but you don't want to flood your production log with unnecessary information.
|
||||
|
||||
[TIP]
|
||||
Rails default log level is +info+ in production mode and +debug+ in development and test mode.
|
||||
The default Rails log level is +info+ in production mode and +debug+ in development and test mode.
|
||||
|
||||
=== Sending Messages
|
||||
|
||||
|
@ -151,7 +156,7 @@ logger.info "Processing the request..."
|
|||
logger.fatal "Terminating application, raised unrecoverable error!!!"
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
A common example:
|
||||
Here's an example of a method instrumented with extra logging:
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
|
@ -176,45 +181,49 @@ class PostsController < ApplicationController
|
|||
end
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Will be logged like this:
|
||||
Here's an example of the log generated by this method:
|
||||
|
||||
[source, log]
|
||||
----------------------------------------------------------------------------
|
||||
Processing PostsController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST]
|
||||
Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b06c4d724596a4
|
||||
Parameters: {"commit"=>"Create", "post"=>{"title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>"0"}, "authenticity_token"=>"2059c1286e93402e389127b1153204e0d1e275dd", "action"=>"create", "controller"=>"posts"}
|
||||
New post: {"updated_at"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>false, "created_at"=>nil}
|
||||
Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGl
|
||||
vbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b06c4d724596a4
|
||||
Parameters: {"commit"=>"Create", "post"=>{"title"=>"Debugging Rails",
|
||||
"body"=>"I'm learning how to print in logs!!!", "published"=>"0"},
|
||||
"authenticity_token"=>"2059c1286e93402e389127b1153204e0d1e275dd", "action"=>"create", "controller"=>"posts"}
|
||||
New post: {"updated_at"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!",
|
||||
"published"=>false, "created_at"=>nil}
|
||||
Post should be valid: true
|
||||
Post Create (0.000443) INSERT INTO "posts" ("updated_at", "title", "body", "published", "created_at") VALUES('2008-09-08 14:52:54', 'Debugging Rails', 'I''m learning how to print in logs!!!', 'f', '2008-09-08 14:52:54')
|
||||
Post Create (0.000443) INSERT INTO "posts" ("updated_at", "title", "body", "published",
|
||||
"created_at") VALUES('2008-09-08 14:52:54', 'Debugging Rails',
|
||||
'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]
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Notice the logged lines, now you can search for any unexpected behavior in the output.
|
||||
|
||||
By now you should know how to use the logs in any environment. Remember to take advantage of the log levels and use them wisely, mostly in production mode.
|
||||
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.
|
||||
|
||||
== Debugging with ruby-debug
|
||||
|
||||
Many times your code may not behave as you expect, sometimes you will try to print in logs, console or view values to make a diagnostic of the problem.
|
||||
When your code is behaving in unexpected ways, you can try printing to logs or the console to diagnose the problem. Unfortunately, there are times when this sort of error tracking is not effective in finding the root cause of a problem. When you actually need to journey into your running source code, the debugger is your best companion.
|
||||
|
||||
Unfortunately, you won't find always the answer you are looking for this way. In that case, you will need to know what's happening and adventure into Rails, in this journey the debugger will be your best companion.
|
||||
|
||||
If you ever wanted to learn about Rails source code but you didn't know where to start, this may be the best way, just debug any request to your application and use this guide to learn how to move in the code you have written but also go deeper into Rails code.
|
||||
The debugger can also help you if you want to learn about the Rails source code but don't know where to start. Just debug any request to your application and use this guide to learn how to move from the code you have written deeper into Rails code.
|
||||
|
||||
=== Setup
|
||||
|
||||
Ruby-debug comes as a gem so to install, just run:
|
||||
The debugger used by Rails, +ruby-debug+, comes as a gem. To install it, just run:
|
||||
|
||||
[source, shell]
|
||||
----------------------------------------------------------------------------
|
||||
$ sudo gem in ruby-debug
|
||||
$ sudo gem install ruby-debug
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
In case you want to download a particular version or get the source code, refer to link:http://rubyforge.org/projects/ruby-debug/[project's page on rubyforge].
|
||||
In case you want to download a particular version or get the source code, refer to the link:http://rubyforge.org/projects/ruby-debug/[project's page on rubyforge].
|
||||
|
||||
Rails has built-in support for ruby-debug since April 28, 2007. Inside any Rails application you can invoke the debugger by calling the `debugger` method.
|
||||
Rails has had built-in support for ruby-debug since Rails 2.0. Inside any Rails application you can invoke the debugger by calling the `debugger` method.
|
||||
|
||||
Let's take a look at an example:
|
||||
Here's an example:
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
|
@ -228,26 +237,32 @@ end
|
|||
|
||||
If you see the message in the console or logs:
|
||||
|
||||
[source, log]
|
||||
----------------------------------------------------------------------------
|
||||
***** Debugger requested, but was not available: Start server with --debugger to enable *****
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Make sure you have started your web server with the option --debugger:
|
||||
Make sure you have started your web server with the option +--debugger+:
|
||||
|
||||
[source, shell]
|
||||
----------------------------------------------------------------------------
|
||||
~/PathTo/rails_project$ script/server --debugger
|
||||
=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
|
||||
=> Rails 2.2.0 application starting on http://0.0.0.0:3000
|
||||
=> Debugger enabled
|
||||
...
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
[TIP]
|
||||
In development mode, you can dynamically `require \'ruby-debug\'` instead of restarting the server, in case it was started without `--debugger`.
|
||||
In development mode, you can dynamically `require \'ruby-debug\'` instead of restarting the server, if it was started without `--debugger`.
|
||||
|
||||
In order to use Rails debugging you'll need to be running either *WEBrick* or *Mongrel*. For the moment, no alternative servers are supported.
|
||||
|
||||
=== The Shell
|
||||
|
||||
As soon as your application calls the `debugger` method, the debugger will be started in a debugger shell inside the terminal window you've fired up your application server and you will be placed in the ruby-debug's prompt `(rdb:n)`. The _n_ is the thread number.
|
||||
As soon as your application calls the `debugger` method, the debugger will be started in a debugger shell inside the terminal window where you launched your application server, and you will be placed at ruby-debug's prompt `(rdb:n)`. The _n_ is the thread number. The prompt will also show you the next line of code that is waiting to run.
|
||||
|
||||
If you got there by a browser request, the browser will be hanging until the debugger has finished and the trace has completely run as any normal request.
|
||||
If you got there by a browser request, the browser tab containing the request will be hung until the debugger has finished and the trace has finished processing the entire request.
|
||||
|
||||
For example:
|
||||
|
||||
|
@ -256,7 +271,7 @@ For example:
|
|||
(rdb:7)
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Now it's time to play and dig into our application. The first we are going to do is ask our debugger for help... so we type: `help` (You didn't see that coming, right?)
|
||||
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?)
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:7) help
|
||||
|
@ -272,11 +287,11 @@ continue edit frame method putl set tmate where
|
|||
----------------------------------------------------------------------------
|
||||
|
||||
[TIP]
|
||||
To view the help menu for any command use `help <command-name>` in active debug mode. For example: _help var_
|
||||
To view the help menu for any command use `help <command-name>` in active debug mode. For example: _+help var+_
|
||||
|
||||
The second command before we move on, is one of the most useful command: `list` (or his shorthand `l`).
|
||||
The next command to learn is one of the most useful: `list`. You can also abbreviate ruby-debug commands by supplying just enough letters to distinguish them from other commands, so you can also use +l+ for the +list+ command.
|
||||
|
||||
This command will give us a starting point of where we are by printing 10 lines centered around the current line; the current line here is line 6 and is marked by =>.
|
||||
This command shows you where you are in the code by printing 10 lines centered around the current line; the current line in this particular case is line 6 and is marked by +=>+.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:7) list
|
||||
|
@ -293,7 +308,7 @@ This command will give us a starting point of where we are by printing 10 lines
|
|||
10 format.xml { render :xml => @posts }
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
If we do it again, this time using just `l`, the next ten lines of the file will be printed out.
|
||||
If you repeat the +list+ command, this time using just `l`, the next ten lines of the file will be printed out.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:7) l
|
||||
|
@ -310,14 +325,15 @@ If we do it again, this time using just `l`, the next ten lines of the file will
|
|||
20 format.html # show.html.erb
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
And so on until the end of the current file, when the end of file is reached, it will start again from the beginning of the file and continue again up to the end, acting as a circular buffer.
|
||||
And so on until the end of the current file. When the end of file is reached, the +list+ command will start again from the beginning of the file and continue again up to the end, treating the file as a circular buffer.
|
||||
|
||||
=== The Context
|
||||
When we start debugging your application, we will be placed in different contexts as you go through the different parts of the stack.
|
||||
|
||||
A context will be created when a stopping point or an event is reached. It has information about the suspended program which enable a debugger to inspect the frame stack, evaluate variables from the perspective of the debugged program, and contains information about the place the debugged program is stopped.
|
||||
When you start debugging your application, you will be placed in different contexts as you go through the different parts of the stack.
|
||||
|
||||
At any time we can call the `backtrace` command (or alias `where`) to print the backtrace of the application, this is very helpful to know how we got where we are. If you ever wondered about how you got somewhere in your code, then `backtrace` is your answer.
|
||||
ruby-debug creates a content when a stopping point or an event is reached. The context has information about the suspended program which enables a debugger to inspect the frame stack, evaluate variables from the perspective of the debugged program, and contains information about the place where the debugged program is stopped.
|
||||
|
||||
At any time you can call the `backtrace` command (or its alias `where`) to print the backtrace of the application. This can be very helpful to know how you got where you are. If you ever wondered about how you got somewhere in your code, then `backtrace` will supply the answer.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:5) where
|
||||
|
@ -332,7 +348,7 @@ At any time we can call the `backtrace` command (or alias `where`) to print the
|
|||
...
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
You move anywhere you want in this trace using the `frame _n_` command, where _n_ is the specified frame number.
|
||||
You move anywhere you want in this trace (thus changing the context) by using the `frame _n_` command, where _n_ is the specified frame number.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:5) frame 2
|
||||
|
@ -340,27 +356,27 @@ You move anywhere you want in this trace using the `frame _n_` command, where _n
|
|||
at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
The available variables are the same as if we were running the code line by line, after all, that's what debugging is.
|
||||
The available variables are the same as if you were running the code line by line. After all, that's what debugging is.
|
||||
|
||||
Moving up and down the stack frame: You can use `up [n]` (`u` for abbreviated) and `down [n]` commands in order to change the context _n_ frames up or down the stack respectively. _n_ defaults to one.
|
||||
Moving up and down the stack frame: You can use `up [n]` (`u` for abbreviated) and `down [n]` commands in order to change the context _n_ frames up or down the stack respectively. _n_ defaults to one. Up in this case is towards higher-numbered stack frames, and down is towards lower-numbered stack frames.
|
||||
|
||||
=== Threads
|
||||
|
||||
The debugger can list, stop, resume and switch between running threads, the command `thread` (or the abbreviated `th`) is used an allows the following options:
|
||||
The debugger can list, stop, resume and switch between running threads by using the command `thread` (or the abbreviated `th`). This command has a handful of options:
|
||||
|
||||
* `thread` shows the current thread.
|
||||
* `thread list` command is used to list all threads and their statuses. The plus + character and the number indicates the current thread of execution.
|
||||
* `thread list` is used to list all threads and their statuses. The plus + character and the number indicates the current thread of execution.
|
||||
* `thread stop _n_` stop thread _n_.
|
||||
* `thread resume _n_` resume thread _n_.
|
||||
* `thread switch _n_` switch thread context to _n_.
|
||||
* `thread resume _n_` resumes thread _n_.
|
||||
* `thread switch _n_` switches the current thread context to _n_.
|
||||
|
||||
This command is very helpful, among other occasions, when you are debugging concurrent threads and need to verify that there are no race conditions in your code.
|
||||
|
||||
=== Inspecting Variables
|
||||
|
||||
Any expression can be evaluated in the current context, just type it!
|
||||
Any expression can be evaluated in the current context. To evaluate an expression, just type it!
|
||||
|
||||
In the following example we will print the instance_variables defined within the current context.
|
||||
This example shows how you can print the instance_variables defined within the current context:
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
@posts = Post.find(:all)
|
||||
|
@ -368,7 +384,7 @@ In the following example we will print the instance_variables defined within the
|
|||
["@_response", "@action_name", "@url", "@_session", "@_cookies", "@performed_render", "@_flash", "@template", "@_params", "@before_filter_chain_aborted", "@request_origin", "@_headers", "@performed_redirect", "@_request"]
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
As you may have figured out, all variables that you can access from a controller are displayed, lets run the next line, we will use `next` (we will get later into this command).
|
||||
As you may have figured out, all of the variables that you can access from a controller are displayed. This list is dynamically updated as you execute code. For example, run the next line using `next` (you'll learn more about this command later in this guide).
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:11) next
|
||||
|
@ -379,19 +395,19 @@ Processing PostsController#index (for 127.0.0.1 at 2008-09-04 19:51:34) [GET]
|
|||
respond_to do |format|
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
And we'll ask again for the instance_variables.
|
||||
And then ask again for the instance_variables:
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:11) instance_variables.include? "@posts"
|
||||
true
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Now +@posts+ is a included in them, because the line defining it was executed.
|
||||
Now +@posts+ is a included in the instance variables, because the line defining it was executed.
|
||||
|
||||
[TIP]
|
||||
You can also step into *irb* mode with the command `irb` (of course!). This way an irb session will be started within the context you invoked it. But you must know that this is an experimental feature.
|
||||
You can also step into *irb* mode with the command `irb` (of course!). This way an irb session will be started within the context you invoked it. But be warned: this is an experimental feature.
|
||||
|
||||
To show variables and their values the `var` method is the most convenient way:
|
||||
The `var` method is the most convenient way to show variables and their values:
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
var
|
||||
|
@ -401,7 +417,7 @@ var
|
|||
(rdb:1) v[ar] l[ocal] show local variables
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
This is a great way for inspecting the values of the current context variables. For example:
|
||||
This is a great way to inspect the values of the current context variables. For example:
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:9) var local
|
||||
|
@ -418,16 +434,16 @@ You can also inspect for an object method this way:
|
|||
----------------------------------------------------------------------------
|
||||
|
||||
[TIP]
|
||||
Commands `p` (print) and `pp` (pretty print) can be used to evaluate Ruby expressions and display the value of variables to the console.
|
||||
The commands `p` (print) and `pp` (pretty print) can be used to evaluate Ruby expressions and display the value of variables to the console.
|
||||
|
||||
We can use also `display` to start watching variables, this is a good way of tracking values of a variable while the execution goes on.
|
||||
You can use also `display` to start watching variables. This is a good way of tracking the values of a variable while the execution goes on.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:1) display @recent_comments
|
||||
1: @recent_comments =
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
The variables inside the displaying list will be printed with their values after we move in the stack. To stop displaying a variable use `undisplay _n_` where _n_ is the variable number (1 in the last example).
|
||||
The variables inside the displaying list will be printed with their values after you move in the stack. To stop displaying a variable use `undisplay _n_` where _n_ is the variable number (1 in the last example).
|
||||
|
||||
=== Step by Step
|
||||
|
||||
|
@ -440,9 +456,9 @@ You can also use `step+ _n_` and `step- _n_` to move forward or backward _n_ ste
|
|||
|
||||
You may also use `next` which is similar to step, but function or method calls that appear within the line of code are executed without stopping. As with step, you may use plus sign to move _n_ steps.
|
||||
|
||||
The difference between `next` and `step` is that `step` stops at the next line of code executed, doing just single step, while `next` moves to the next line without descending inside methods.
|
||||
The difference between `next` and `step` is that `step` stops at the next line of code executed, doing just a single step, while `next` moves to the next line without descending inside methods.
|
||||
|
||||
Lets run the next line in this example:
|
||||
For example, consider this block of code with an included +debugger+ statement:
|
||||
|
||||
[source, ruby]
|
||||
----------------------------------------------------------------------------
|
||||
|
@ -462,7 +478,7 @@ end
|
|||
----------------------------------------------------------------------------
|
||||
|
||||
[TIP]
|
||||
You can use ruby-debug while using script/console but remember to `require "ruby-debug"` before calling `debugger` method.
|
||||
You can use ruby-debug while using script/console. Just remember to `require "ruby-debug"` before calling the `debugger` method.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
/PathTo/project $ script/console
|
||||
|
@ -476,7 +492,7 @@ Loading development environment (Rails 2.1.0)
|
|||
)
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Now we are where we wanted to be, lets look around.
|
||||
With the code stopped, take a look around:
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:1) list
|
||||
|
@ -491,7 +507,7 @@ Now we are where we wanted to be, lets look around.
|
|||
13 end
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
We are at the end of the line, but... was this line executed? We can inspect the instance variables.
|
||||
You are at the end of the line, but... was this line executed? You can inspect the instance variables.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:1) var instance
|
||||
|
@ -499,7 +515,7 @@ We are at the end of the line, but... was this line executed? We can inspect the
|
|||
@attributes_cache = {}
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
+@recent_comments+ hasn't been defined yet, so we can assure this line hasn't been executed yet, lets move on this code.
|
||||
+@recent_comments+ hasn't been defined yet, so it's clear that this line hasn't been executed yet. Use the +next+ command to move on in the code:
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:1) next
|
||||
|
@ -512,15 +528,15 @@ We are at the end of the line, but... was this line executed? We can inspect the
|
|||
@recent_comments = []
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Now we can see how +@comments+ relationship was loaded and @recent_comments defined because the line was executed.
|
||||
Now you can see that the +@comments+ relationship was loaded and @recent_comments defined because the line was executed.
|
||||
|
||||
In case we want deeper in the stack trace we can move single `steps` and go into Rails code, this is the best way for finding bugs in your code, or maybe in Ruby or Rails.
|
||||
If you want to go deeper into the stack trace you can move single `steps`, through your calling methods and into Rails code. This is one of the best ways to find bugs in your code, or perhaps in Ruby or Rails.
|
||||
|
||||
=== Breakpoints
|
||||
|
||||
A breakpoint makes your application stop whenever a certain point in the program is reached and the debugger shell is invoked in that line.
|
||||
A breakpoint makes your application stop whenever a certain point in the program is reached. The debugger shell is invoked in that line.
|
||||
|
||||
You can add breakpoints dynamically with the command `break` (or just `b`), there are 3 possible ways of adding breakpoints manually:
|
||||
You can add breakpoints dynamically with the command `break` (or just `b`). There are 3 possible ways of adding breakpoints manually:
|
||||
|
||||
* `break line`: set breakpoint in the _line_ in the current source file.
|
||||
* `break file:line [if expression]`: set breakpoint in the _line_ number inside the _file_. If an _expression_ is given it must evaluated to _true_ to fire up the debugger.
|
||||
|
@ -531,7 +547,7 @@ You can add breakpoints dynamically with the command `break` (or just `b`), ther
|
|||
Breakpoint 1 file /PathTo/project/vendor/rails/actionpack/lib/action_controller/filters.rb, line 10
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Use `info breakpoints _n_` or `info break _n_` lo list breakpoints, is _n_ is defined it shows that breakpoints, otherwise all breakpoints are listed.
|
||||
Use `info breakpoints _n_` or `info break _n_` to list breakpoints. If you supply a number, it lists that breakpoint. Otherwise it lists all breakpoints.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:5) info breakpoints
|
||||
|
@ -539,7 +555,7 @@ Num Enb What
|
|||
1 y at filters.rb:10
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Deleting breakpoints: use the command `delete _n_` to remove the breakpoint number _n_ or all of them if _n_ is not specified.
|
||||
To delete breakpoints: use the command `delete _n_` to remove the breakpoint number _n_. If no number is specified, it deletes all breakpoints that are currently active..
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
(rdb:5) delete 1
|
||||
|
@ -547,57 +563,60 @@ Deleting breakpoints: use the command `delete _n_` to remove the breakpoint numb
|
|||
No breakpoints.
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
Enabling/Disabling breakpoints:
|
||||
You can also enable or disable breakpoints:
|
||||
|
||||
* `enable breakpoints`: allow a list _breakpoints_ or all of them if none specified, to stop your program (this is the default state when you create a breakpoint).
|
||||
* `enable breakpoints`: allow a list _breakpoints_ or all of them if no list is specified, to stop your program. This is the default state when you create a breakpoint.
|
||||
* `disable breakpoints`: the _breakpoints_ will have no effect on your program.
|
||||
|
||||
=== Catching Exceptions
|
||||
|
||||
The command `catch exception-name` (or just `cat exception-name`) can be used to intercept an exception of type _exception-name_ when there would otherwise be is no handler for it.
|
||||
|
||||
To list existent catchpoints use `catch`.
|
||||
To list all active catchpoints use `catch`.
|
||||
|
||||
=== Resuming Execution
|
||||
|
||||
There are two ways to resume execution of an application that is stopped in the debugger:
|
||||
|
||||
* `continue` [line-specification] (or `c`): resume program execution, at the address where your script last stopped; any breakpoints set at that address are bypassed. The optional argument line-specification allows you to specify a line number to set a one-time breakpoint which is deleted when that breakpoint is reached.
|
||||
* `finish` [frame-number] (or `fin`): execute until selected stack frame returns. If no frame number is given, we run until the currently selected frame returns. The currently selected frame starts out the most-recent frame or 0 if no frame positioning (e.g up, down or frame) has been performed. If a frame number is given we run until frame frames returns.
|
||||
* `finish` [frame-number] (or `fin`): execute until the selected stack frame returns. If no frame number is given, the application run until the currently selected frame returns. The currently selected frame starts out the most-recent frame or 0 if no frame positioning (e.g up, down or frame) has been performed. If a frame number is given it will run until the specified frame returns.
|
||||
|
||||
=== Editing
|
||||
|
||||
At any time, you may use any of this commands to edit the code you are evaluating:
|
||||
Two commands allow you to open code from the debugger into an editor:
|
||||
|
||||
* `edit [file:line]`: edit _file_ using the editor specified by the EDITOR environment variable. A specific _line_ can also be given.
|
||||
* `tmate _n_` (abbreviated `tm`): open the current file in TextMate. It uses n-th frame if _n_ is specified.
|
||||
|
||||
=== Quitting
|
||||
To exit the debugger, use the `quit` command (abbreviated `q`), or alias `exit`.
|
||||
|
||||
To exit the debugger, use the `quit` command (abbreviated `q`), or its alias `exit`.
|
||||
|
||||
A simple quit tries to terminate all threads in effect. Therefore your server will be stopped and you will have to start it again.
|
||||
|
||||
=== Settings
|
||||
|
||||
There are some settings that can be configured in ruby-debug to make it easier to debug your code, being among others useful options:
|
||||
There are some settings that can be configured in ruby-debug to make it easier to debug your code. Here are a few of the available options:
|
||||
|
||||
* `set reload`: Reload source code when changed.
|
||||
* `set autolist`: Execute `list` command on every breakpoint.
|
||||
* `set listsize _n_`: Set number of source lines to list by default _n_.
|
||||
* `set forcestep`: Make sure `next` and `step` commands always move to a new line
|
||||
* `set listsize _n_`: Set number of source lines to list by default to _n_.
|
||||
* `set forcestep`: Make sure the `next` and `step` commands always move to a new line
|
||||
|
||||
You can see the full list by using `help set` or `help set subcommand` to inspect any of them.
|
||||
You can see the full list by using `help set`. Use `help set _subcommand_` to learn about a particular +set+ command.
|
||||
|
||||
[TIP]
|
||||
You can include any number of this configuration lines inside a `.rdebugrc` file in your HOME directory, and ruby-debug will read it every time it is loaded
|
||||
You can include any number of these configuration lines inside a `.rdebugrc` file in your HOME directory. ruby-debug will read this file every time it is loaded. and configure itself accordingly.
|
||||
|
||||
The following lines are recommended to be included in `.rdebugrc`:
|
||||
Here's a good start for an `.rdebugrc`:
|
||||
|
||||
[source, log]
|
||||
----------------------------------------------------------------------------
|
||||
set autolist
|
||||
set forcestep
|
||||
set listsize 25
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
|
||||
== References
|
||||
|
||||
* link:http://www.datanoise.com/ruby-debug[ruby-debug Homepage]
|
||||
|
@ -614,4 +633,5 @@ set listsize 25
|
|||
|
||||
http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/5[Lighthouse ticket]
|
||||
|
||||
* October 19, 2008: Copy editing pass by link:../authors.html#mgunderloy[Mike Gunderloy]
|
||||
* September 16, 2008: initial version by link:../authors.html#miloops[Emilio Tagua]
|
|
@ -192,7 +192,7 @@ Readonly is a find option that you can set in order to make that instance of the
|
|||
[source, ruby]
|
||||
Client.find(:first, :readonly => true)
|
||||
|
||||
If you assign this record to a variable `client` calling the following code will raise an ActiveRecord::ReadOnlyRecord
|
||||
If you assign this record to a variable `client` calling the following code will raise an ActiveRecord::ReadOnlyRecord:
|
||||
|
||||
[source, ruby]
|
||||
client = Client.find(:first, :readonly => true)
|
||||
|
@ -273,14 +273,33 @@ When you define a has_many association on a model you get the find method and dy
|
|||
|
||||
== Named Scopes
|
||||
|
||||
There was mention of named scopes earlier in "First, Last and All" where we covered the named scopes of `first`, `last` and `all` which were aliases of `find(:first)`, `find(:last)`, `find(:all)` respectively. Now we'll cover adding named scopes to the models in the application. Let's say we want to find all clients who are not locked to do this we would use this code:
|
||||
In this section we'll cover adding named scopes to the models in the application. Let's say we want to find all clients who are male we would use this code:
|
||||
|
||||
[source, ruby]
|
||||
class Client < ActiveRecord::Base
|
||||
named_scope :males, :conditions => { :gender => "male" }
|
||||
end
|
||||
|
||||
And we could call it like `Client.males` to get all the clients who are male.
|
||||
|
||||
If we wanted to find all the clients who are active, we could use this:
|
||||
|
||||
[source,ruby]
|
||||
class Client < ActiveRecord::Base
|
||||
named_scope :unlocked, :conditions => { :locked => false }
|
||||
named_scope :active, :conditions => { :active => true }
|
||||
end
|
||||
|
||||
We would call this new named_scope by doing `Client.unlocked` and this will do the same query as if we just used `Client.find(:all, :conditions => ["unlocked = ?", false])`. Please be aware that the conditions syntax in named_scope and find is different and the two are not interchangeable. If you want to find the first client within this named scope you could do `Client.first.unlocked`. This is possible because named scopes are stackable.
|
||||
We would call this new named_scope by doing `Client.active` and this will do the same query as if we just used `Client.find(:all, :conditions => ["active = ?", true])`. Please be aware that the conditions syntax in named_scope and find is different and the two are not interchangeable. If you want to find the first client within this named scope you could do `Client.active.first`.
|
||||
|
||||
and then if we wanted to find all the clients who are active and male we could stack the named scopes like this:
|
||||
|
||||
[source, ruby]
|
||||
Client.males.active
|
||||
|
||||
If you would then like to do a `find` on that subset of clients, you can. Just like an association, named scopes allow you to call `find` on a set of records:
|
||||
|
||||
[source, ruby]
|
||||
Client.males.active.find(:all, :conditions => ["age > ?", params[:age]])
|
||||
|
||||
Now observe the following code:
|
||||
|
||||
|
@ -293,11 +312,20 @@ What we see here is what looks to be a standard named scope that defines a metho
|
|||
|
||||
[source, ruby]
|
||||
class Client < ActiveRecord::Base
|
||||
named_scope :recent, lambda { { :conditions => ["created_at > ?", 2.weeks.ago] } } }
|
||||
named_scope :recent, lambda { { :conditions => ["created_at > ?", 2.weeks.ago] } }
|
||||
end
|
||||
|
||||
And now every time the recent named scope is called, because it's wrapped in a lambda block this code will be parsed every time so you'll get actually 2 weeks ago from the code execution, not 2 weeks ago from the time the model was loaded.
|
||||
|
||||
In a named scope you can use `:include` and `:joins` options just like in find.
|
||||
|
||||
[source, ruby]
|
||||
class Client < ActiveRecord::Base
|
||||
named_scope :active_within_2_weeks, :joins => :order, lambda { { :conditions => ["orders.created_at > ?", 2.weeks.ago] } }
|
||||
end
|
||||
|
||||
This method called as `Client.active_within_2_weeks` will return all clients who have placed orders in the past 2 weeks.
|
||||
|
||||
If you want to pass a named scope a compulsory argument, just specify it as a block parameter like this:
|
||||
|
||||
[source, ruby]
|
||||
|
@ -316,6 +344,7 @@ This will work with `Client.recent(2.weeks.ago)` and `Client.recent` with the la
|
|||
|
||||
Remember that named scopes are stackable, so you will be able to do `Client.recent(2.weeks.ago).unlocked` to find all clients created between right now and 2 weeks ago and have their locked field set to false.
|
||||
|
||||
|
||||
== Existance of Objects
|
||||
|
||||
If you simply want to check for the existance of the object there's a method called `exists?`. This method will query the database using the same query as find, but instead of returning an object or collection of objects it will return either true or false.
|
||||
|
@ -387,3 +416,6 @@ Thanks to Mike Gunderloy for his tips on creating this guide.
|
|||
=== Thursday, 09 October 2008
|
||||
1. Wrote section about lock option and tidied up "Making it all work together" section.
|
||||
2. Added section on using count.
|
||||
|
||||
=== Tuesday, 21 October 2008
|
||||
1. Extended named scope guide by adding :include and :joins and find sub-sections.
|
|
@ -164,7 +164,7 @@ File/Folder Purpose
|
|||
+log/+ Application log files.
|
||||
+public/+ The only folder seen to the world as-is. This is where your images, javascript, stylesheets (CSS), and other static files go.
|
||||
+script/+ Scripts provided by Rails to do recurring tasks, such as benchmarking, plugin installation, and starting the console or the web server.
|
||||
+test/+ Unit tests, fixtures, and other test apparatus. These are covered in link:../testing_rails_applications/testing_rails_applications.html[Testing Rails Applications]
|
||||
+test/+ Unit tests, fixtures, and other test apparatus. These are covered in link:../testing_rails_applications.html[Testing Rails Applications]
|
||||
+tmp/+ Temporary files
|
||||
+vendor/+ A place for third-party code. In a typical Rails application, this includes Ruby Gems, the Rails source code (if you install it into your project) and plugins containing additional prepackaged functionality.
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -298,7 +298,7 @@ This line illustrates one tiny bit of the "convention over configuration" approa
|
|||
|
||||
Now if you navigate to +http://localhost:3000+ in your browser, you'll see the +home/index+ view.
|
||||
|
||||
NOTE: For more information about routing, refer to link:../routing/routing_outside_in.html[Rails Routing from the Outside In].
|
||||
NOTE: For more information about routing, refer to link:../routing_outside_in.html[Rails Routing from the Outside In].
|
||||
|
||||
== Getting Up and Running Quickly With Scaffolding
|
||||
|
||||
|
@ -362,7 +362,7 @@ class CreatePosts < ActiveRecord::Migration
|
|||
end
|
||||
-------------------------------------------------------
|
||||
|
||||
If you were to translate that into words, it says something like: when this migration is run, create a table named +posts+ with two string columns (+name+ and +title+) and a text column (+content+), and generate timestamp fields to track record creation and updating. You can learn the detailed syntax for migrations in the link:../migrations/migrations.html[Rails Database Migrations] guide.
|
||||
If you were to translate that into words, it says something like: when this migration is run, create a table named +posts+ with two string columns (+name+ and +title+) and a text column (+content+), and generate timestamp fields to track record creation and updating. You can learn the detailed syntax for migrations in the link:../migrations.html[Rails Database Migrations] guide.
|
||||
|
||||
At this point, you need to do two things: create the database and run the migration. You can use rake commands at the terminal for both of those tasks:
|
||||
|
||||
|
@ -417,14 +417,13 @@ Rails includes methods to help you validate the data that you send to models. Op
|
|||
|
||||
[source, ruby]
|
||||
-------------------------------------------------------
|
||||
class Comment < ActiveRecord::Base
|
||||
belongs_to :post
|
||||
validates_presence_of :commenter, :body
|
||||
validates_length_of :commenter, :minimum => 5
|
||||
class Post < ActiveRecord::Base
|
||||
validates_presence_of :name, :title
|
||||
validates_length_of :title, :minimum => 5
|
||||
end
|
||||
-------------------------------------------------------
|
||||
|
||||
These changes will ensure that all comments have a body and a commenter, and that the commenter is at least five characters long. Rails can validate a variety of conditions in a model, including the presence or uniqueness of columns, their format, and the existence of associated objects.
|
||||
These changes will ensure that all posts have a name and a title, and that the title is at least five characters long. Rails can validate a variety of conditions in a model, including the presence or uniqueness of columns, their format, and the existence of associated objects.
|
||||
|
||||
=== Using the Console
|
||||
|
||||
|
@ -439,19 +438,19 @@ After the console loads, you can use it to work with your application's models:
|
|||
|
||||
[source, shell]
|
||||
-------------------------------------------------------
|
||||
>> c = Comment.create(:body => "A new comment")
|
||||
=> #<Comment id: nil, commenter: nil, body: "A new comment",
|
||||
post_id: nil, created_at: nil, updated_at: nil>
|
||||
>> c.save
|
||||
>> p = Post.create(:content => "A new post")
|
||||
=> #<Post id: nil, name: nil, title: nil, content: "A new post",
|
||||
created_at: nil, updated_at: nil>
|
||||
>> p.save
|
||||
=> false
|
||||
>> c.errors
|
||||
=> #<ActiveRecord::Errors:0x227be7c @base=#<Comment id: nil,
|
||||
commenter: nil, body: "A new comment", post_id: nil, created_at: nil,
|
||||
updated_at: nil>, @errors={"commenter"=>["can't be blank",
|
||||
>> p.errors
|
||||
=> #<ActiveRecord::Errors:0x23bcf0c @base=#<Post id: nil, name: nil,
|
||||
title: nil, content: "A new post", created_at: nil, updated_at: nil>,
|
||||
@errors={"name"=>["can't be blank"], "title"=>["can't be blank",
|
||||
"is too short (minimum is 5 characters)"]}>
|
||||
-------------------------------------------------------
|
||||
|
||||
This code shows creating a new +Comment+ instance, attempting to save it and getting +false+ for a return value (indicating that the save failed), and inspecting the +errors+ of the comment.
|
||||
This code shows creating a new +Post+ instance, attempting to save it and getting +false+ for a return value (indicating that the save failed), and inspecting the +errors+ of the post.
|
||||
|
||||
TIP: Unlike the development web server, the console does not automatically load your code afresh for each line. If you make changes, type +reload!+ at the console prompt to load them.
|
||||
|
||||
|
@ -473,7 +472,7 @@ end
|
|||
|
||||
This code sets the +@posts+ instance variable to an array of all posts in the database. +Post.find(:all)+ or +Post.all+ calls the +Post+ model to return all of the posts that are currently in the database, with no limiting conditions.
|
||||
|
||||
TIP: For more information on finding records with Active Record, see link:../activerecord/finders.html[Active Record Finders].
|
||||
TIP: For more information on finding records with Active Record, see link:../finders.html[Active Record Finders].
|
||||
|
||||
The +respond_to+ block handles both HTML and XML calls to this action. If you borwse to +http://localhost:3000/posts.xml+, you'll see all of the posts in XML format. The HTML format looks for a view in +app/views/posts/+ with a name that corresponds to the action name. Rails makes all of the instance variables from the action available to the view. Here's +app/view/posts/index.html.erb+:
|
||||
|
||||
|
@ -511,7 +510,7 @@ This view iterates over the contents of the +@posts+ array to display content an
|
|||
* +link_to+ builds a hyperlink to a particular destination
|
||||
* +edit_post_path+ is a helper that Rails provides as part of RESTful routing. You’ll see a variety of these helpers for the different actions that the controller includes.
|
||||
|
||||
TIP: For more details on the rendering process, see link:../actionview/layouts_and_rendering.html[Layouts and Rendering in Rails].
|
||||
TIP: For more details on the rendering process, see link:../layouts_and_rendering.html[Layouts and Rendering in Rails].
|
||||
|
||||
=== Customizing the Layout
|
||||
|
||||
|
@ -818,13 +817,15 @@ You'll need to edit the +post.rb+ file to add the other side of the association:
|
|||
[source, ruby]
|
||||
-------------------------------------------------------
|
||||
class Post < ActiveRecord::Base
|
||||
validates_presence_of :name, :title
|
||||
validates_length_of :title, :minimum => 5
|
||||
has_many :comments
|
||||
end
|
||||
-------------------------------------------------------
|
||||
|
||||
These two declarations enable a good bit of automatic behavior. For example, if you have an instance variable +@post+ containing a post, you can retrieve all the comments belonging to that post as the array +@post.comments+.
|
||||
|
||||
TIP: For more information on Active Record associations, see the link:../activerecord/association_basics.html+[Active Record Associations] guide.
|
||||
TIP: For more information on Active Record associations, see the link:../association_basics.html+[Active Record Associations] guide.
|
||||
|
||||
=== Adding a Route
|
||||
|
||||
|
@ -839,7 +840,7 @@ end
|
|||
|
||||
This creates +comments+ as a _nested resource_ within +posts+. This is another part of capturing the hierarchical relationship that exists between posts and comments.
|
||||
|
||||
TIP: For more information on routing, see the link:../routing/routing_outside_in[Rails Routing from the Outside In] guide.
|
||||
TIP: For more information on routing, see the link:../routing_outside_in[Rails Routing from the Outside In] guide.
|
||||
|
||||
=== Generating a Controller
|
||||
|
||||
|
@ -930,6 +931,15 @@ end
|
|||
|
||||
You'll see a bit more complexity here than you did in the controller for posts. That's a side-effect of the nesting that you've set up; each request for a comment has to keep track of the post to which the comment is attached.
|
||||
|
||||
In addition, the code takes advantage of some of the methods available for an association. For example, in the +new+ method, it calls
|
||||
|
||||
[source, ruby]
|
||||
-------------------------------------------------------
|
||||
@comment = @post.comments.build
|
||||
-------------------------------------------------------
|
||||
|
||||
This creates a new +Comment+ object _and_ sets up the +post_id+ field to have the +id+ from the specified +Post+ object in a single operation.
|
||||
|
||||
=== Building Views
|
||||
|
||||
Because you skipped scaffolding, you'll need to build views for comments "by hand." Invoking +script/generate controller+ will give you skeleton views, but they'll be devoid of actual content. Here's a first pass at fleshing out the comment views.
|
Before Width: | Height: | Size: 329 B After Width: | Height: | Size: 329 B |
Before Width: | Height: | Size: 361 B After Width: | Height: | Size: 361 B |
Before Width: | Height: | Size: 565 B After Width: | Height: | Size: 565 B |
Before Width: | Height: | Size: 617 B After Width: | Height: | Size: 617 B |
Before Width: | Height: | Size: 623 B After Width: | Height: | Size: 623 B |
Before Width: | Height: | Size: 411 B After Width: | Height: | Size: 411 B |
Before Width: | Height: | Size: 640 B After Width: | Height: | Size: 640 B |
Before Width: | Height: | Size: 353 B After Width: | Height: | Size: 353 B |
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 350 B |
Before Width: | Height: | Size: 345 B After Width: | Height: | Size: 345 B |
Before Width: | Height: | Size: 348 B After Width: | Height: | Size: 348 B |
Before Width: | Height: | Size: 355 B After Width: | Height: | Size: 355 B |
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
Before Width: | Height: | Size: 357 B After Width: | Height: | Size: 357 B |
Before Width: | Height: | Size: 357 B After Width: | Height: | Size: 357 B |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 60 B |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |