提交 db5a98e6 编写于 作者: P Pratik Naik

Merge docrails

上级 07298fd0
......@@ -52,7 +52,7 @@ NOTE: This output will seem very familiar when we get to the `generate` command.
=== server ===
Let's try it! The `server` command launches a small web server written in Ruby named WEBrick which was also installed when you installed Rails. You'll use this any time you want to view your work through a web browser.
Let's try it! The `server` command launches a small web server named WEBrick which comes bundled with Ruby. You'll use this any time you want to view your work through a web browser.
NOTE: WEBrick isn't your only option for serving Rails. We'll get to that in a later section. [XXX: which section]
......@@ -99,7 +99,7 @@ Using generators will save you a large amount of time by writing *boilerplate co
Let's make our own controller with the controller generator. But what command should we use? Let's ask the generator:
NOTE: All Rails console utilities have help text. For commands that require a lot of input to run correctly, you can try the command without any parameters (like `rails` or `./script/generate`). For others, you can try adding `--help` or `-h` to the end, as in `./script/server --help`.
NOTE: All Rails console utilities have help text. As with most *NIX utilities, you can try adding `--help` or `-h` to the end, for example `./script/server --help`.
[source,shell]
------------------------------------------------------
......@@ -200,24 +200,47 @@ Examples:
creates a Post model with a string title, text body, and published flag.
------------------------------------------------------
Let's set up a simple model called "HighScore" that will keep track of our highest score on video games we play. Then we'll wire up our controller and view to modify and list our scores.
But instead of generating a model directly (which we'll be doing later), let's set up a scaffold. A *scaffold* in Rails is a full set of model, database migration for that model, controller to manipulate it, views to view and manipulate the data, and a test suite for each of the above.
Let's set up a simple resource called "HighScore" that will keep track of our highest score on video games we play.
[source,shell]
------------------------------------------------------
$ ./script/generate model HighScore id:integer game:string score:integer
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/high_score.rb
create test/unit/high_score_test.rb
create test/fixtures/high_scores.yml
create db/migrate
create db/migrate/20081126032945_create_high_scores.rb
$ ./script/generate scaffold HighScore game:string score:integer
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/high_scores
create app/views/layouts/
exists test/functional/
create test/unit/
create public/stylesheets/
create app/views/high_scores/index.html.erb
create app/views/high_scores/show.html.erb
create app/views/high_scores/new.html.erb
create app/views/high_scores/edit.html.erb
create app/views/layouts/high_scores.html.erb
create public/stylesheets/scaffold.css
create app/controllers/high_scores_controller.rb
create test/functional/high_scores_controller_test.rb
create app/helpers/high_scores_helper.rb
route map.resources :high_scores
dependency model
exists app/models/
exists test/unit/
create test/fixtures/
create app/models/high_score.rb
create test/unit/high_score_test.rb
create test/fixtures/high_scores.yml
exists db/migrate
create db/migrate/20081217071914_create_high_scores.rb
------------------------------------------------------
Taking it from the top, we have the *models* directory, where all of your data models live. *test/unit*, where all the unit tests live (gasp! -- unit tests!), fixtures for those tests, a test, the *migrate* directory, where the database-modifying migrations live, and a migration to create the `high_scores` table with the right fields.
Taking it from the top - the generator checks that there exist the directories for models, controllers, helpers, layouts, functional and unit tests, stylesheets, creates the views, controller, model and database migration for HighScore (creating the `high_scores` table and fields), takes care of the route for the *resource*, and new tests for everything.
The migration requires that we *migrate*, that is, run some Ruby code (living in that `20081217071914_create_high_scores.rb`) to modify the schema of our database. Which database? The sqlite3 database that Rails will create for you when we run the `rake db:migrate` command. We'll talk more about Rake in-depth in a little while.
The migration requires that we *migrate*, that is, run some Ruby code (living in that `20081126032945_create_high_scores.rb`) to modify the schema of our database. Which database? The sqlite3 database that Rails will create for you when we run the `rake db:migrate` command. We'll talk more about Rake in-depth in a little while.
NOTE: Hey. Install the sqlite3-ruby gem while you're at it. `gem install sqlite3-ruby`
[source,shell]
------------------------------------------------------
......@@ -231,23 +254,87 @@ $ rake db:migrate
NOTE: Let's talk about unit tests. Unit tests are code that tests and makes assertions about code. In unit testing, we take a little part of code, say a method of a model, and test its inputs and outputs. Unit tests are your friend. The sooner you make peace with the fact that your quality of life will drastically increase when you unit test your code, the better. Seriously. We'll make one in a moment.
Yo! Let's shove a small table into our greeting controller and view, listing our sweet scores.
Let's see the interface Rails created for us. ./script/server; http://localhost:3000/high_scores
[source,ruby]
We can create new high scores (55,160 on Space Invaders!)
=== console ===
The `console` command lets you interact with your Rails application from the command line. On the underside, `script/console` uses IRB, so if you've ever used it, you'll be right at home. This is useful for testing out quick ideas with code and changing data server-side without touching the website.
=== dbconsole ===
`dbconsole` figures out which database you're using and drops you into whichever command line interface you would use with it (and figures out the command line parameters to give to it, too!). It supports MySQL, PostgreSQL, SQLite and SQLite3.
=== plugin ===
The `plugin` command simplifies plugin management; think a miniature version of the Gem utility. Let's walk through installing a plugin. You can call the sub-command *discover*, which sifts through repositories looking for plugins, or call *source* to add a specific repository of plugins, or you can specify the plugin location directly.
Let's say you're creating a website for a client who wants a small accounting system. Every event having to do with money must be logged, and must never be deleted. Wouldn't it be great if we could override the behavior of a model to never actually take its record out of the database, but *instead*, just set a field?
There is such a thing! The plugin we're installing is called "acts_as_paranoid", and it lets models implement a "deleted_at" column that gets set when you call destroy. Later, when calling find, the plugin will tack on a database check to filter out "deleted" things.
[source,shell]
------------------------------------------------------
$ ./script/plugin install http://svn.techno-weenie.net/projects/plugins/acts_as_paranoid
+ ./CHANGELOG
+ ./MIT-LICENSE
...
...
------------------------------------------------------
class GreetingController < ApplicationController
def hello
if request.post?
score = HighScore.new(params[:high_score])
if score.save
flash[:notice] = "New score posted!"
end
end
@scores = HighScore.find(:all)
end
end
=== runner ===
`runner` runs Ruby code in the context of Rails non-interactively. For instance:
[source,shell]
------------------------------------------------------
$ ./script/runner "Model.long_running_method"
------------------------------------------------------
=== destroy ===
Think of `destroy` as the opposite of `generate`. It'll figure out what generate did, and undo it. Believe you-me, the creation of this tutorial used this command many times!
XXX: Go with scaffolding instead, modifying greeting controller for high scores seems dumb.
[source,shell]
------------------------------------------------------
$ ./script/generate model Oops
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/oops.rb
create test/unit/oops_test.rb
create test/fixtures/oops.yml
exists db/migrate
create db/migrate/20081221040817_create_oops.rb
$ ./script/destroy model Oops
notempty db/migrate
notempty db
rm db/migrate/20081221040817_create_oops.rb
rm test/fixtures/oops.yml
rm test/unit/oops_test.rb
rm app/models/oops.rb
notempty test/fixtures
notempty test
notempty test/unit
notempty test
notempty app/models
notempty app
------------------------------------------------------
=== about ===
Check it: Version numbers for Ruby, RubyGems, Rails, the Rails subcomponents, your application's folder, the current Rails environment name, your app's database adapter, and schema version! `about` is useful when you need to ask help, check if a security patch might affect you, or when you need some stats for an existing Rails installation.
[source,shell]
------------------------------------------------------
$ ./script/about
About your application's environment
Ruby version 1.8.6 (i486-linux)
RubyGems version 1.3.1
Rails version 2.2.0
Active Record version 2.2.0
Action Pack version 2.2.0
Active Resource version 2.2.0
Action Mailer version 2.2.0
Active Support version 2.2.0
Edge Rails revision unknown
Application root /home/commandsapp
Environment development
Database adapter sqlite3
Database schema version 20081217073400
------------------------------------------------------
\ No newline at end of file
......@@ -247,7 +247,7 @@ A nice thing about `f.text_field` and other helper methods is that they will pre
Relying on record identification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 *a resource*.
In the previous chapter we handled the Article model. This model is directly available to users of our application, so -- following the best practices for developing with Rails -- we should declare it *a resource*.
When dealing with RESTful resources, our calls to `form_for` can get significantly easier if we rely on *record identification*. In short, we can just pass the model instance and have Rails figure out model name and the rest:
......@@ -291,15 +291,13 @@ Here we have a list of cities where their names are presented to the user, but i
The select tag and options
~~~~~~~~~~~~~~~~~~~~~~~~~~
The most generic helper is `select_tag`, which -- as the name implies -- simply generates the `SELECT` tag that encapsulates the options:
The most generic helper is `select_tag`, which -- as the name implies -- simply generates the `SELECT` tag that encapsulates an options string:
----------------------------------------------------------------------------
<%= select_tag(:city_id, '<option value="1">Lisabon</option>...') %>
----------------------------------------------------------------------------
This is a start, but it doesn't dynamically create our option tags. We had to pass them in as a string.
We can generate option tags with the `options_for_select` helper:
This is a start, but it doesn't dynamically create our option tags. We can generate option tags with the `options_for_select` helper:
----------------------------------------------------------------------------
<%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...]) %>
......@@ -311,9 +309,9 @@ output:
...
----------------------------------------------------------------------------
For input data we used a nested array where each element has two elements: visible value (name) and internal value (ID).
For input data we used a nested array where each item has two elements: option text (city name) and option value (city id).
Now you can combine `select_tag` and `options_for_select` to achieve the desired, complete markup:
Knowing this, you can combine `select_tag` and `options_for_select` to achieve the desired, complete markup:
----------------------------------------------------------------------------
<%= select_tag(:city_id, options_for_select(...)) %>
......@@ -333,13 +331,114 @@ output:
So whenever Rails sees that the internal value of an option being generated matches this value, it will add the `selected` attribute to that option.
Select boxes for dealing with models
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Select helpers for dealing with models
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Until now we've covered how to make generic select boxes, but in most cases our form controls will be tied to a specific database model. So, to continue from our previous examples, let's assume that we have a "Person" model with a `city_id` attribute.
Consistent with other form helpers, when dealing with models we drop the `"_tag"` suffix from `select_tag` that we used in previous examples:
----------------------------------------------------------------------------
...
# controller:
@person = Person.new(:city_id => 2)
# view:
<%= select(:person, :city_id, [['Lisabon', 1], ['Madrid', 2], ...]) %>
----------------------------------------------------------------------------
Notice that the third parameter, the options array, is the same kind of argument we pass to `options_for_select`. One thing that we have as an advantage here is that we don't have to worry about pre-selecting the correct city if the user already has one -- Rails will do this for us by reading from `@person.city_id` attribute.
As before, if we were to use `select` helper on a form builder scoped to `@person` object, the syntax would be:
----------------------------------------------------------------------------
# select on a form builder
<%= f.select(:city_id, ...) %>
----------------------------------------------------------------------------
Option tags from a collection of arbitrary objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Until now we were generating option tags from nested arrays with the help of `options_for_select` method. Data in our array were raw values:
----------------------------------------------------------------------------
<%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...]) %>
----------------------------------------------------------------------------
But what if we had a *City* model (perhaps an ActiveRecord one) and we wanted to generate option tags from a collection of those objects? One solution would be to make a nested array by iterating over them:
----------------------------------------------------------------------------
<% cities_array = City.find(:all).map { |city| [city.name, city.id] } %>
<%= options_for_select(cities_array) %>
----------------------------------------------------------------------------
This is a perfectly valid solution, but Rails provides us with a less verbose alternative: `options_from_collection_for_select`. This helper expects a collection of arbitrary objects and two additional arguments: the names of the methods to read the option *value* and *text* from, respectively:
----------------------------------------------------------------------------
<%= options_from_collection_for_select(City.all, :id, :name) %>
----------------------------------------------------------------------------
As the name implies, this only generates option tags. A method to go along with it is `collection_select`:
----------------------------------------------------------------------------
<%= collection_select(:person, :city_id, City.all, :id, :name) %>
----------------------------------------------------------------------------
...
\ No newline at end of file
To recap, `options_from_collection_for_select` are to `collection_select` what `options_for_select` are to `select`.
Time zone and country select
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To leverage time zone support in Rails, we have to ask our users what time zone they are in. Doing so would require generating select options from a list of pre-defined TimeZone objects using `collection_select`, but we can simply use the `time_zone_select` helper that already wraps this:
----------------------------------------------------------------------------
<%= time_zone_select(:person, :city_id) %>
----------------------------------------------------------------------------
There is also `time_zone_options_for_select` helper for a more manual (therefore more customizable) way of doing this. Read the API documentation to learn about the possible arguments for these two methods.
When it comes to country select, Rails _used_ to have the built-in helper `country_select` but was extracted to a plugin.
TODO: plugin URL
Miscellaneous
-------------
File upload form
~~~~~~~~~~~~~~~~
:multipart - If set to true, the enctype is set to "multipart/form-data".
Scoping out form controls with `fields_for`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Creates a scope around a specific model object like `form_for`, but doesn’t create the form tags themselves. This makes `fields_for` suitable for specifying additional model objects in the same form:
Making custom form builders
~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can also build forms using a customized FormBuilder class. Subclass FormBuilder and override or define some more helpers, then use your custom builder. For example, let’s say you made a helper to automatically add labels to form inputs.
----------------------------------------------------------------------------
<% form_for :person, @person, :url => { :action => "update" }, :builder => LabellingFormBuilder do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= text_area :person, :biography %>
<%= check_box_tag "person[admin]", @person.company.admin? %>
<% end %>
----------------------------------------------------------------------------
* `form_for` within a namespace
----------------------------------------------------------------------------
select_tag(name, option_tags = nil, html_options = { :multiple, :disabled })
select(object, method, choices, options = {}, html_options = {})
options_for_select(container, selected = nil)
collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
options_from_collection_for_select(collection, value_method, text_method, selected = nil)
time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
----------------------------------------------------------------------------
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册