mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #15215 from JohnKellyFerguson/rename-posts-to-articles
Documentation: Rename Posts to Articles
This commit is contained in:
commit
88a3a5db5e
20 changed files with 657 additions and 654 deletions
|
@ -6,4 +6,8 @@
|
|||
|
||||
*Alex Riabov*
|
||||
|
||||
* Change all non-HTTP method 'post' references to 'article'.
|
||||
|
||||
*John Kelly Ferguson*
|
||||
|
||||
Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/guides/CHANGELOG.md) for previous changes.
|
||||
|
|
|
@ -28,22 +28,22 @@ For each controller there is an associated directory in the `app/views` director
|
|||
Let's take a look at what Rails does by default when creating a new resource using the scaffold generator:
|
||||
|
||||
```bash
|
||||
$ bin/rails generate scaffold post
|
||||
$ bin/rails generate scaffold article
|
||||
[...]
|
||||
invoke scaffold_controller
|
||||
create app/controllers/posts_controller.rb
|
||||
create app/controllers/articles_controller.rb
|
||||
invoke erb
|
||||
create app/views/posts
|
||||
create app/views/posts/index.html.erb
|
||||
create app/views/posts/edit.html.erb
|
||||
create app/views/posts/show.html.erb
|
||||
create app/views/posts/new.html.erb
|
||||
create app/views/posts/_form.html.erb
|
||||
create app/views/articles
|
||||
create app/views/articles/index.html.erb
|
||||
create app/views/articles/edit.html.erb
|
||||
create app/views/articles/show.html.erb
|
||||
create app/views/articles/new.html.erb
|
||||
create app/views/articles/_form.html.erb
|
||||
[...]
|
||||
```
|
||||
|
||||
There is a naming convention for views in Rails. Typically, the views share their name with the associated controller action, as you can see above.
|
||||
For example, the index controller action of the `posts_controller.rb` will use the `index.html.erb` view file in the `app/views/posts` directory.
|
||||
For example, the index controller action of the `articles_controller.rb` will use the `index.html.erb` view file in the `app/views/articles` directory.
|
||||
The complete HTML returned to the client is composed of a combination of this ERB file, a layout template that wraps it, and all the partials that the view may reference. Later on this guide you can find a more detailed documentation of each one of these three components.
|
||||
|
||||
|
||||
|
@ -276,23 +276,23 @@ Partial Layouts
|
|||
|
||||
Partials can have their own layouts applied to them. These layouts are different than the ones that are specified globally for the entire action, but they work in a similar fashion.
|
||||
|
||||
Let's say we're displaying a post on a page, that should be wrapped in a `div` for display purposes. First, we'll create a new `Post`:
|
||||
Let's say we're displaying an article on a page, that should be wrapped in a `div` for display purposes. First, we'll create a new `Article`:
|
||||
|
||||
```ruby
|
||||
Post.create(body: 'Partial Layouts are cool!')
|
||||
Article.create(body: 'Partial Layouts are cool!')
|
||||
```
|
||||
|
||||
In the `show` template, we'll render the `_post` partial wrapped in the `box` layout:
|
||||
In the `show` template, we'll render the `_article` partial wrapped in the `box` layout:
|
||||
|
||||
**posts/show.html.erb**
|
||||
**articles/show.html.erb**
|
||||
|
||||
```erb
|
||||
<%= render partial: 'post', layout: 'box', locals: {post: @post} %>
|
||||
<%= render partial: 'article', layout: 'box', locals: {article: @article} %>
|
||||
```
|
||||
|
||||
The `box` layout simply wraps the `_post` partial in a `div`:
|
||||
The `box` layout simply wraps the `_article` partial in a `div`:
|
||||
|
||||
**posts/_box.html.erb**
|
||||
**articles/_box.html.erb**
|
||||
|
||||
```html+erb
|
||||
<div class='box'>
|
||||
|
@ -300,13 +300,13 @@ The `box` layout simply wraps the `_post` partial in a `div`:
|
|||
</div>
|
||||
```
|
||||
|
||||
The `_post` partial wraps the post's `body` in a `div` with the `id` of the post using the `div_for` helper:
|
||||
The `_article` partial wraps the article's `body` in a `div` with the `id` of the article using the `div_for` helper:
|
||||
|
||||
**posts/_post.html.erb**
|
||||
**articles/_article.html.erb**
|
||||
|
||||
```html+erb
|
||||
<%= div_for(post) do %>
|
||||
<p><%= post.body %></p>
|
||||
<%= div_for(article) do %>
|
||||
<p><%= article.body %></p>
|
||||
<% end %>
|
||||
```
|
||||
|
||||
|
@ -314,22 +314,22 @@ this would output the following:
|
|||
|
||||
```html
|
||||
<div class='box'>
|
||||
<div id='post_1'>
|
||||
<div id='article_1'>
|
||||
<p>Partial Layouts are cool!</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Note that the partial layout has access to the local `post` variable that was passed into the `render` call. However, unlike application-wide layouts, partial layouts still have the underscore prefix.
|
||||
Note that the partial layout has access to the local `article` variable that was passed into the `render` call. However, unlike application-wide layouts, partial layouts still have the underscore prefix.
|
||||
|
||||
You can also render a block of code within a partial layout instead of calling `yield`. For example, if we didn't have the `_post` partial, we could do this instead:
|
||||
You can also render a block of code within a partial layout instead of calling `yield`. For example, if we didn't have the `_article` partial, we could do this instead:
|
||||
|
||||
**posts/show.html.erb**
|
||||
**articles/show.html.erb**
|
||||
|
||||
```html+erb
|
||||
<% render(layout: 'box', locals: {post: @post}) do %>
|
||||
<%= div_for(post) do %>
|
||||
<p><%= post.body %></p>
|
||||
<% render(layout: 'box', locals: {article: @article}) do %>
|
||||
<%= div_for(article) do %>
|
||||
<p><%= article.body %></p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
```
|
||||
|
@ -356,18 +356,18 @@ This module provides methods for generating container tags, such as `div`, for y
|
|||
|
||||
Renders a container tag that relates to your Active Record Object.
|
||||
|
||||
For example, given `@post` is the object of `Post` class, you can do:
|
||||
For example, given `@article` is the object of `Article` class, you can do:
|
||||
|
||||
```html+erb
|
||||
<%= content_tag_for(:tr, @post) do %>
|
||||
<td><%= @post.title %></td>
|
||||
<%= content_tag_for(:tr, @article) do %>
|
||||
<td><%= @article.title %></td>
|
||||
<% end %>
|
||||
```
|
||||
|
||||
This will generate this HTML output:
|
||||
|
||||
```html
|
||||
<tr id="post_1234" class="post">
|
||||
<tr id="article_1234" class="article">
|
||||
<td>Hello World!</td>
|
||||
</tr>
|
||||
```
|
||||
|
@ -375,34 +375,34 @@ This will generate this HTML output:
|
|||
You can also supply HTML attributes as an additional option hash. For example:
|
||||
|
||||
```html+erb
|
||||
<%= content_tag_for(:tr, @post, class: "frontpage") do %>
|
||||
<td><%= @post.title %></td>
|
||||
<%= content_tag_for(:tr, @article, class: "frontpage") do %>
|
||||
<td><%= @article.title %></td>
|
||||
<% end %>
|
||||
```
|
||||
|
||||
Will generate this HTML output:
|
||||
|
||||
```html
|
||||
<tr id="post_1234" class="post frontpage">
|
||||
<tr id="article_1234" class="article frontpage">
|
||||
<td>Hello World!</td>
|
||||
</tr>
|
||||
```
|
||||
|
||||
You can pass a collection of Active Record objects. This method will loop through your objects and create a container for each of them. For example, given `@posts` is an array of two `Post` objects:
|
||||
You can pass a collection of Active Record objects. This method will loop through your objects and create a container for each of them. For example, given `@articles` is an array of two `Article` objects:
|
||||
|
||||
```html+erb
|
||||
<%= content_tag_for(:tr, @posts) do |post| %>
|
||||
<td><%= post.title %></td>
|
||||
<%= content_tag_for(:tr, @articles) do |article| %>
|
||||
<td><%= article.title %></td>
|
||||
<% end %>
|
||||
```
|
||||
|
||||
Will generate this HTML output:
|
||||
|
||||
```html
|
||||
<tr id="post_1234" class="post">
|
||||
<tr id="article_1234" class="article">
|
||||
<td>Hello World!</td>
|
||||
</tr>
|
||||
<tr id="post_1235" class="post">
|
||||
<tr id="article_1235" class="article">
|
||||
<td>Ruby on Rails Rocks!</td>
|
||||
</tr>
|
||||
```
|
||||
|
@ -412,15 +412,15 @@ Will generate this HTML output:
|
|||
This is actually a convenient method which calls `content_tag_for` internally with `:div` as the tag name. You can pass either an Active Record object or a collection of objects. For example:
|
||||
|
||||
```html+erb
|
||||
<%= div_for(@post, class: "frontpage") do %>
|
||||
<td><%= @post.title %></td>
|
||||
<%= div_for(@article, class: "frontpage") do %>
|
||||
<td><%= @article.title %></td>
|
||||
<% end %>
|
||||
```
|
||||
|
||||
Will generate this HTML output:
|
||||
|
||||
```html
|
||||
<div id="post_1234" class="post frontpage">
|
||||
<div id="article_1234" class="article frontpage">
|
||||
<td>Hello World!</td>
|
||||
</div>
|
||||
```
|
||||
|
@ -590,14 +590,14 @@ This helper makes building an Atom feed easy. Here's a full usage example:
|
|||
**config/routes.rb**
|
||||
|
||||
```ruby
|
||||
resources :posts
|
||||
resources :articles
|
||||
```
|
||||
|
||||
**app/controllers/posts_controller.rb**
|
||||
**app/controllers/articles_controller.rb**
|
||||
|
||||
```ruby
|
||||
def index
|
||||
@posts = Post.all
|
||||
@articles = Article.all
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
|
@ -606,20 +606,20 @@ def index
|
|||
end
|
||||
```
|
||||
|
||||
**app/views/posts/index.atom.builder**
|
||||
**app/views/articles/index.atom.builder**
|
||||
|
||||
```ruby
|
||||
atom_feed do |feed|
|
||||
feed.title("Posts Index")
|
||||
feed.updated((@posts.first.created_at))
|
||||
feed.title("Articles Index")
|
||||
feed.updated((@articles.first.created_at))
|
||||
|
||||
@posts.each do |post|
|
||||
feed.entry(post) do |entry|
|
||||
entry.title(post.title)
|
||||
entry.content(post.body, type: 'html')
|
||||
@articles.each do |article|
|
||||
feed.entry(article) do |entry|
|
||||
entry.title(article.title)
|
||||
entry.content(article.body, type: 'html')
|
||||
|
||||
entry.author do |author|
|
||||
author.name(post.author_name)
|
||||
author.name(article.author_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -697,7 +697,7 @@ For example, let's say we have a standard application layout, but also a special
|
|||
</html>
|
||||
```
|
||||
|
||||
**app/views/posts/special.html.erb**
|
||||
**app/views/articles/special.html.erb**
|
||||
|
||||
```html+erb
|
||||
<p>This is a special page.</p>
|
||||
|
@ -714,7 +714,7 @@ For example, let's say we have a standard application layout, but also a special
|
|||
Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based attribute.
|
||||
|
||||
```ruby
|
||||
date_select("post", "published_on")
|
||||
date_select("article", "published_on")
|
||||
```
|
||||
|
||||
#### datetime_select
|
||||
|
@ -722,7 +722,7 @@ date_select("post", "published_on")
|
|||
Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a specified datetime-based attribute.
|
||||
|
||||
```ruby
|
||||
datetime_select("post", "published_on")
|
||||
datetime_select("article", "published_on")
|
||||
```
|
||||
|
||||
#### distance_of_time_in_words
|
||||
|
@ -904,10 +904,10 @@ The params hash has a nested person value, which can therefore be accessed with
|
|||
Returns a checkbox tag tailored for accessing a specified attribute.
|
||||
|
||||
```ruby
|
||||
# Let's say that @post.validated? is 1:
|
||||
check_box("post", "validated")
|
||||
# => <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
|
||||
# <input name="post[validated]" type="hidden" value="0" />
|
||||
# Let's say that @article.validated? is 1:
|
||||
check_box("article", "validated")
|
||||
# => <input type="checkbox" id="article_validated" name="article[validated]" value="1" />
|
||||
# <input name="article[validated]" type="hidden" value="0" />
|
||||
```
|
||||
|
||||
#### fields_for
|
||||
|
@ -939,7 +939,7 @@ file_field(:user, :avatar)
|
|||
Creates a form and a scope around a specific model object that is used as a base for questioning about values for the fields.
|
||||
|
||||
```html+erb
|
||||
<%= form_for @post do |f| %>
|
||||
<%= form_for @article do |f| %>
|
||||
<%= f.label :title, 'Title' %>:
|
||||
<%= f.text_field :title %><br>
|
||||
<%= f.label :body, 'Body' %>:
|
||||
|
@ -961,8 +961,8 @@ hidden_field(:user, :token)
|
|||
Returns a label tag tailored for labelling an input field for a specified attribute.
|
||||
|
||||
```ruby
|
||||
label(:post, :title)
|
||||
# => <label for="post_title">Title</label>
|
||||
label(:article, :title)
|
||||
# => <label for="article_title">Title</label>
|
||||
```
|
||||
|
||||
#### password_field
|
||||
|
@ -979,11 +979,11 @@ password_field(:login, :pass)
|
|||
Returns a radio button tag for accessing a specified attribute.
|
||||
|
||||
```ruby
|
||||
# Let's say that @post.category returns "rails":
|
||||
radio_button("post", "category", "rails")
|
||||
radio_button("post", "category", "java")
|
||||
# => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
|
||||
# <input type="radio" id="post_category_java" name="post[category]" value="java" />
|
||||
# Let's say that @article.category returns "rails":
|
||||
radio_button("article", "category", "rails")
|
||||
radio_button("article", "category", "java")
|
||||
# => <input type="radio" id="article_category_rails" name="article[category]" value="rails" checked="checked" />
|
||||
# <input type="radio" id="article_category_java" name="article[category]" value="java" />
|
||||
```
|
||||
|
||||
#### text_area
|
||||
|
@ -1002,8 +1002,8 @@ text_area(:comment, :text, size: "20x30")
|
|||
Returns an input tag of the "text" type tailored for accessing a specified attribute.
|
||||
|
||||
```ruby
|
||||
text_field(:post, :title)
|
||||
# => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" />
|
||||
text_field(:article, :title)
|
||||
# => <input type="text" id="article_title" name="article[title]" value="#{@article.title}" />
|
||||
```
|
||||
|
||||
#### email_field
|
||||
|
@ -1035,28 +1035,28 @@ Returns `select` and `option` tags for the collection of existing return values
|
|||
Example object structure for use with this method:
|
||||
|
||||
```ruby
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
belongs_to :author
|
||||
end
|
||||
|
||||
class Author < ActiveRecord::Base
|
||||
has_many :posts
|
||||
has_many :articles
|
||||
def name_with_initial
|
||||
"#{first_name.first}. #{last_name}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Sample usage (selecting the associated Author for an instance of Post, `@post`):
|
||||
Sample usage (selecting the associated Author for an instance of Article, `@article`):
|
||||
|
||||
```ruby
|
||||
collection_select(:post, :author_id, Author.all, :id, :name_with_initial, {prompt: true})
|
||||
collection_select(:article, :author_id, Author.all, :id, :name_with_initial, {prompt: true})
|
||||
```
|
||||
|
||||
If `@post.author_id` is 1, this would return:
|
||||
If `@article.author_id` is 1, this would return:
|
||||
|
||||
```html
|
||||
<select name="post[author_id]">
|
||||
<select name="article[author_id]">
|
||||
<option value="">Please select</option>
|
||||
<option value="1" selected="selected">D. Heinemeier Hansson</option>
|
||||
<option value="2">D. Thomas</option>
|
||||
|
@ -1071,33 +1071,33 @@ Returns `radio_button` tags for the collection of existing return values of `met
|
|||
Example object structure for use with this method:
|
||||
|
||||
```ruby
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
belongs_to :author
|
||||
end
|
||||
|
||||
class Author < ActiveRecord::Base
|
||||
has_many :posts
|
||||
has_many :articles
|
||||
def name_with_initial
|
||||
"#{first_name.first}. #{last_name}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Sample usage (selecting the associated Author for an instance of Post, `@post`):
|
||||
Sample usage (selecting the associated Author for an instance of Article, `@article`):
|
||||
|
||||
```ruby
|
||||
collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial)
|
||||
collection_radio_buttons(:article, :author_id, Author.all, :id, :name_with_initial)
|
||||
```
|
||||
|
||||
If `@post.author_id` is 1, this would return:
|
||||
If `@article.author_id` is 1, this would return:
|
||||
|
||||
```html
|
||||
<input id="post_author_id_1" name="post[author_id]" type="radio" value="1" checked="checked" />
|
||||
<label for="post_author_id_1">D. Heinemeier Hansson</label>
|
||||
<input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
|
||||
<label for="post_author_id_2">D. Thomas</label>
|
||||
<input id="post_author_id_3" name="post[author_id]" type="radio" value="3" />
|
||||
<label for="post_author_id_3">M. Clark</label>
|
||||
<input id="article_author_id_1" name="article[author_id]" type="radio" value="1" checked="checked" />
|
||||
<label for="article_author_id_1">D. Heinemeier Hansson</label>
|
||||
<input id="article_author_id_2" name="article[author_id]" type="radio" value="2" />
|
||||
<label for="article_author_id_2">D. Thomas</label>
|
||||
<input id="article_author_id_3" name="article[author_id]" type="radio" value="3" />
|
||||
<label for="article_author_id_3">M. Clark</label>
|
||||
```
|
||||
|
||||
#### collection_check_boxes
|
||||
|
@ -1107,34 +1107,34 @@ Returns `check_box` tags for the collection of existing return values of `method
|
|||
Example object structure for use with this method:
|
||||
|
||||
```ruby
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
has_and_belongs_to_many :authors
|
||||
end
|
||||
|
||||
class Author < ActiveRecord::Base
|
||||
has_and_belongs_to_many :posts
|
||||
has_and_belongs_to_many :articles
|
||||
def name_with_initial
|
||||
"#{first_name.first}. #{last_name}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Sample usage (selecting the associated Authors for an instance of Post, `@post`):
|
||||
Sample usage (selecting the associated Authors for an instance of Article, `@article`):
|
||||
|
||||
```ruby
|
||||
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
|
||||
collection_check_boxes(:article, :author_ids, Author.all, :id, :name_with_initial)
|
||||
```
|
||||
|
||||
If `@post.author_ids` is [1], this would return:
|
||||
If `@article.author_ids` is [1], this would return:
|
||||
|
||||
```html
|
||||
<input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" />
|
||||
<label for="post_author_ids_1">D. Heinemeier Hansson</label>
|
||||
<input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
|
||||
<label for="post_author_ids_2">D. Thomas</label>
|
||||
<input id="post_author_ids_3" name="post[author_ids][]" type="checkbox" value="3" />
|
||||
<label for="post_author_ids_3">M. Clark</label>
|
||||
<input name="post[author_ids][]" type="hidden" value="" />
|
||||
<input id="article_author_ids_1" name="article[author_ids][]" type="checkbox" value="1" checked="checked" />
|
||||
<label for="article_author_ids_1">D. Heinemeier Hansson</label>
|
||||
<input id="article_author_ids_2" name="article[author_ids][]" type="checkbox" value="2" />
|
||||
<label for="article_author_ids_2">D. Thomas</label>
|
||||
<input id="article_author_ids_3" name="article[author_ids][]" type="checkbox" value="3" />
|
||||
<label for="article_author_ids_3">M. Clark</label>
|
||||
<input name="article[author_ids][]" type="hidden" value="" />
|
||||
```
|
||||
|
||||
#### country_options_for_select
|
||||
|
@ -1222,13 +1222,13 @@ Create a select tag and a series of contained option tags for the provided objec
|
|||
Example:
|
||||
|
||||
```ruby
|
||||
select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {include_blank: true})
|
||||
select("article", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {include_blank: true})
|
||||
```
|
||||
|
||||
If `@post.person_id` is 1, this would become:
|
||||
If `@article.person_id` is 1, this would become:
|
||||
|
||||
```html
|
||||
<select name="post[person_id]">
|
||||
<select name="article[person_id]">
|
||||
<option value=""></option>
|
||||
<option value="1" selected="selected">David</option>
|
||||
<option value="2">Sam</option>
|
||||
|
@ -1303,10 +1303,10 @@ file_field_tag 'attachment'
|
|||
Starts a form tag that points the action to an url configured with `url_for_options` just like `ActionController::Base#url_for`.
|
||||
|
||||
```html+erb
|
||||
<%= form_tag '/posts' do %>
|
||||
<%= form_tag '/articles' do %>
|
||||
<div><%= submit_tag 'Save' %></div>
|
||||
<% end %>
|
||||
# => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
|
||||
# => <form action="/articles" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
|
||||
```
|
||||
|
||||
#### hidden_field_tag
|
||||
|
@ -1368,8 +1368,8 @@ select_tag "people", "<option>David</option>"
|
|||
Creates a submit button with the text provided as the caption.
|
||||
|
||||
```ruby
|
||||
submit_tag "Publish this post"
|
||||
# => <input name="commit" type="submit" value="Publish this post" />
|
||||
submit_tag "Publish this article"
|
||||
# => <input name="commit" type="submit" value="Publish this article" />
|
||||
```
|
||||
|
||||
#### text_area_tag
|
||||
|
@ -1377,8 +1377,8 @@ submit_tag "Publish this post"
|
|||
Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions.
|
||||
|
||||
```ruby
|
||||
text_area_tag 'post'
|
||||
# => <textarea id="post" name="post"></textarea>
|
||||
text_area_tag 'article'
|
||||
# => <textarea id="article" name="article"></textarea>
|
||||
```
|
||||
|
||||
#### text_field_tag
|
||||
|
@ -1602,7 +1602,7 @@ Localized Views
|
|||
|
||||
Action View has the ability render different templates depending on the current locale.
|
||||
|
||||
For example, suppose you have a `PostsController` with a show action. By default, calling this action will render `app/views/posts/show.html.erb`. But if you set `I18n.locale = :de`, then `app/views/posts/show.de.html.erb` will be rendered instead. If the localized template isn't present, the undecorated version will be used. This means you're not required to provide localized views for all cases, but they will be preferred and used if available.
|
||||
For example, suppose you have a `ArticlesController` with a show action. By default, calling this action will render `app/views/articles/show.html.erb`. But if you set `I18n.locale = :de`, then `app/views/articles/show.de.html.erb` will be rendered instead. If the localized template isn't present, the undecorated version will be used. This means you're not required to provide localized views for all cases, but they will be preferred and used if available.
|
||||
|
||||
You can use the same technique to localize the rescue files in your public directory. For example, setting `I18n.locale = :de` and creating `public/500.de.html` and `public/404.de.html` would allow you to have localized rescue pages.
|
||||
|
||||
|
@ -1616,6 +1616,6 @@ def set_expert_locale
|
|||
end
|
||||
```
|
||||
|
||||
Then you could create special views like `app/views/posts/show.expert.html.erb` that would only be displayed to expert users.
|
||||
Then you could create special views like `app/views/articles/show.expert.html.erb` that would only be displayed to expert users.
|
||||
|
||||
You can read more about the Rails Internationalization (I18n) API [here](i18n.html).
|
||||
|
|
|
@ -82,13 +82,13 @@ by underscores. Examples:
|
|||
* Model Class - Singular with the first letter of each word capitalized (e.g.,
|
||||
`BookClub`).
|
||||
|
||||
| Model / Class | Table / Schema |
|
||||
| ------------- | -------------- |
|
||||
| `Post` | `posts` |
|
||||
| `LineItem` | `line_items` |
|
||||
| `Deer` | `deers` |
|
||||
| `Mouse` | `mice` |
|
||||
| `Person` | `people` |
|
||||
| Model / Class | Table / Schema |
|
||||
| ---------------- | -------------- |
|
||||
| `Article` | `articles` |
|
||||
| `LineItem` | `line_items` |
|
||||
| `Deer` | `deers` |
|
||||
| `Mouse` | `mice` |
|
||||
| `Person` | `people` |
|
||||
|
||||
|
||||
### Schema Conventions
|
||||
|
@ -120,9 +120,9 @@ to Active Record instances:
|
|||
* `(association_name)_type` - Stores the type for
|
||||
[polymorphic associations](association_basics.html#polymorphic-associations).
|
||||
* `(table_name)_count` - Used to cache the number of belonging objects on
|
||||
associations. For example, a `comments_count` column in a `Post` class that
|
||||
associations. For example, a `comments_count` column in a `Articles` class that
|
||||
has many instances of `Comment` will cache the number of existent comments
|
||||
for each post.
|
||||
for each article.
|
||||
|
||||
NOTE: While these column names are optional, they are in fact reserved by Active Record. Steer clear of reserved keywords unless you want the extra functionality. For example, `type` is a reserved keyword used to designate a table using Single Table Inheritance (STI). If you are not using STI, try an analogous keyword like "context", that may still accurately describe the data you are modeling.
|
||||
|
||||
|
|
|
@ -261,27 +261,27 @@ WARNING. Any exception that is not `ActiveRecord::Rollback` will be re-raised by
|
|||
Relational Callbacks
|
||||
--------------------
|
||||
|
||||
Callbacks work through model relationships, and can even be defined by them. Suppose an example where a user has many posts. A user's posts should be destroyed if the user is destroyed. Let's add an `after_destroy` callback to the `User` model by way of its relationship to the `Post` model:
|
||||
Callbacks work through model relationships, and can even be defined by them. Suppose an example where a user has many articles. A user's articles should be destroyed if the user is destroyed. Let's add an `after_destroy` callback to the `User` model by way of its relationship to the `Article` model:
|
||||
|
||||
```ruby
|
||||
class User < ActiveRecord::Base
|
||||
has_many :posts, dependent: :destroy
|
||||
has_many :articles, dependent: :destroy
|
||||
end
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
after_destroy :log_destroy_action
|
||||
|
||||
def log_destroy_action
|
||||
puts 'Post destroyed'
|
||||
puts 'Article destroyed'
|
||||
end
|
||||
end
|
||||
|
||||
>> user = User.first
|
||||
=> #<User id: 1>
|
||||
>> user.posts.create!
|
||||
=> #<Post id: 1, user_id: 1>
|
||||
>> user.articles.create!
|
||||
=> #<Article id: 1, user_id: 1>
|
||||
>> user.destroy
|
||||
Post destroyed
|
||||
Article destroyed
|
||||
=> #<User id: 1>
|
||||
```
|
||||
|
||||
|
@ -328,7 +328,7 @@ When writing conditional callbacks, it is possible to mix both `:if` and `:unles
|
|||
```ruby
|
||||
class Comment < ActiveRecord::Base
|
||||
after_create :send_email_to_author, if: :author_wants_emails?,
|
||||
unless: Proc.new { |comment| comment.post.ignore_comments? }
|
||||
unless: Proc.new { |comment| comment.article.ignore_comments? }
|
||||
end
|
||||
```
|
||||
|
||||
|
|
|
@ -472,8 +472,8 @@ Client.where('locked' => true)
|
|||
In the case of a belongs_to relationship, an association key can be used to specify the model if an Active Record object is used as the value. This method works with polymorphic relationships as well.
|
||||
|
||||
```ruby
|
||||
Post.where(author: author)
|
||||
Author.joins(:posts).where(posts: { author: author })
|
||||
Article.where(author: author)
|
||||
Author.joins(:articles).where(articles: { author: author })
|
||||
```
|
||||
|
||||
NOTE: The values cannot be symbols. For example, you cannot do `Client.where(status: :active)`.
|
||||
|
@ -511,7 +511,7 @@ SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
|
|||
`NOT` SQL queries can be built by `where.not`.
|
||||
|
||||
```ruby
|
||||
Post.where.not(author: author)
|
||||
Article.where.not(author: author)
|
||||
```
|
||||
|
||||
In other words, this query can be generated by calling `where` with no argument, then immediately chain with `not` passing `where` conditions.
|
||||
|
@ -690,32 +690,32 @@ Overriding Conditions
|
|||
You can specify certain conditions to be removed using the `unscope` method. For example:
|
||||
|
||||
```ruby
|
||||
Post.where('id > 10').limit(20).order('id asc').except(:order)
|
||||
Article.where('id > 10').limit(20).order('id asc').except(:order)
|
||||
```
|
||||
|
||||
The SQL that would be executed:
|
||||
|
||||
```sql
|
||||
SELECT * FROM posts WHERE id > 10 LIMIT 20
|
||||
SELECT * FROM articles WHERE id > 10 LIMIT 20
|
||||
|
||||
# Original query without `unscope`
|
||||
SELECT * FROM posts WHERE id > 10 ORDER BY id asc LIMIT 20
|
||||
SELECT * FROM articles WHERE id > 10 ORDER BY id asc LIMIT 20
|
||||
|
||||
```
|
||||
|
||||
You can additionally unscope specific where clauses. For example:
|
||||
|
||||
```ruby
|
||||
Post.where(id: 10, trashed: false).unscope(where: :id)
|
||||
# SELECT "posts".* FROM "posts" WHERE trashed = 0
|
||||
Article.where(id: 10, trashed: false).unscope(where: :id)
|
||||
# SELECT "articles".* FROM "articles" WHERE trashed = 0
|
||||
```
|
||||
|
||||
A relation which has used `unscope` will affect any relation it is
|
||||
merged in to:
|
||||
|
||||
```ruby
|
||||
Post.order('id asc').merge(Post.unscope(:order))
|
||||
# SELECT "posts".* FROM "posts"
|
||||
Article.order('id asc').merge(Article.unscope(:order))
|
||||
# SELECT "articles".* FROM "articles"
|
||||
```
|
||||
|
||||
### `only`
|
||||
|
@ -723,16 +723,16 @@ Post.order('id asc').merge(Post.unscope(:order))
|
|||
You can also override conditions using the `only` method. For example:
|
||||
|
||||
```ruby
|
||||
Post.where('id > 10').limit(20).order('id desc').only(:order, :where)
|
||||
Article.where('id > 10').limit(20).order('id desc').only(:order, :where)
|
||||
```
|
||||
|
||||
The SQL that would be executed:
|
||||
|
||||
```sql
|
||||
SELECT * FROM posts WHERE id > 10 ORDER BY id DESC
|
||||
SELECT * FROM articles WHERE id > 10 ORDER BY id DESC
|
||||
|
||||
# Original query without `only`
|
||||
SELECT "posts".* FROM "posts" WHERE (id > 10) ORDER BY id desc LIMIT 20
|
||||
SELECT "articles".* FROM "articles" WHERE (id > 10) ORDER BY id desc LIMIT 20
|
||||
|
||||
```
|
||||
|
||||
|
@ -741,25 +741,25 @@ SELECT "posts".* FROM "posts" WHERE (id > 10) ORDER BY id desc LIMIT 20
|
|||
The `reorder` method overrides the default scope order. For example:
|
||||
|
||||
```ruby
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
..
|
||||
..
|
||||
has_many :comments, -> { order('posted_at DESC') }
|
||||
end
|
||||
|
||||
Post.find(10).comments.reorder('name')
|
||||
Article.find(10).comments.reorder('name')
|
||||
```
|
||||
|
||||
The SQL that would be executed:
|
||||
|
||||
```sql
|
||||
SELECT * FROM comments WHERE post_id = 10 ORDER BY name
|
||||
SELECT * FROM articles WHERE id = 10 ORDER BY name
|
||||
```
|
||||
|
||||
In case the `reorder` clause is not used, the SQL executed would be:
|
||||
|
||||
```sql
|
||||
SELECT * FROM comments WHERE post_id = 10 ORDER BY posted_at DESC
|
||||
SELECT * FROM articles WHERE id = 10 ORDER BY posted_at DESC
|
||||
```
|
||||
|
||||
### `reverse_order`
|
||||
|
@ -795,25 +795,25 @@ This method accepts **no** arguments.
|
|||
The `rewhere` method overrides an existing, named where condition. For example:
|
||||
|
||||
```ruby
|
||||
Post.where(trashed: true).rewhere(trashed: false)
|
||||
Article.where(trashed: true).rewhere(trashed: false)
|
||||
```
|
||||
|
||||
The SQL that would be executed:
|
||||
|
||||
```sql
|
||||
SELECT * FROM posts WHERE `trashed` = 0
|
||||
SELECT * FROM articles WHERE `trashed` = 0
|
||||
```
|
||||
|
||||
In case the `rewhere` clause is not used,
|
||||
|
||||
```ruby
|
||||
Post.where(trashed: true).where(trashed: false)
|
||||
Article.where(trashed: true).where(trashed: false)
|
||||
```
|
||||
|
||||
the SQL executed would be:
|
||||
|
||||
```sql
|
||||
SELECT * FROM posts WHERE `trashed` = 1 AND `trashed` = 0
|
||||
SELECT * FROM articles WHERE `trashed` = 1 AND `trashed` = 0
|
||||
```
|
||||
|
||||
Null Relation
|
||||
|
@ -822,21 +822,21 @@ Null Relation
|
|||
The `none` method returns a chainable relation with no records. Any subsequent conditions chained to the returned relation will continue generating empty relations. This is useful in scenarios where you need a chainable response to a method or a scope that could return zero results.
|
||||
|
||||
```ruby
|
||||
Post.none # returns an empty Relation and fires no queries.
|
||||
Article.none # returns an empty Relation and fires no queries.
|
||||
```
|
||||
|
||||
```ruby
|
||||
# The visible_posts method below is expected to return a Relation.
|
||||
@posts = current_user.visible_posts.where(name: params[:name])
|
||||
# The visible_articles method below is expected to return a Relation.
|
||||
@articles = current_user.visible_articles.where(name: params[:name])
|
||||
|
||||
def visible_posts
|
||||
def visible_articles
|
||||
case role
|
||||
when 'Country Manager'
|
||||
Post.where(country: country)
|
||||
Article.where(country: country)
|
||||
when 'Reviewer'
|
||||
Post.published
|
||||
Article.published
|
||||
when 'Bad User'
|
||||
Post.none # => returning [] or nil breaks the caller code in this case
|
||||
Article.none # => returning [] or nil breaks the caller code in this case
|
||||
end
|
||||
end
|
||||
```
|
||||
|
@ -963,21 +963,21 @@ WARNING: This method only works with `INNER JOIN`.
|
|||
|
||||
Active Record lets you use the names of the [associations](association_basics.html) defined on the model as a shortcut for specifying `JOIN` clauses for those associations when using the `joins` method.
|
||||
|
||||
For example, consider the following `Category`, `Post`, `Comment`, `Guest` and `Tag` models:
|
||||
For example, consider the following `Category`, `Article`, `Comment`, `Guest` and `Tag` models:
|
||||
|
||||
```ruby
|
||||
class Category < ActiveRecord::Base
|
||||
has_many :posts
|
||||
has_many :articles
|
||||
end
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
belongs_to :category
|
||||
has_many :comments
|
||||
has_many :tags
|
||||
end
|
||||
|
||||
class Comment < ActiveRecord::Base
|
||||
belongs_to :post
|
||||
belongs_to :article
|
||||
has_one :guest
|
||||
end
|
||||
|
||||
|
@ -986,7 +986,7 @@ class Guest < ActiveRecord::Base
|
|||
end
|
||||
|
||||
class Tag < ActiveRecord::Base
|
||||
belongs_to :post
|
||||
belongs_to :article
|
||||
end
|
||||
```
|
||||
|
||||
|
@ -995,64 +995,64 @@ Now all of the following will produce the expected join queries using `INNER JOI
|
|||
#### Joining a Single Association
|
||||
|
||||
```ruby
|
||||
Category.joins(:posts)
|
||||
Category.joins(:articles)
|
||||
```
|
||||
|
||||
This produces:
|
||||
|
||||
```sql
|
||||
SELECT categories.* FROM categories
|
||||
INNER JOIN posts ON posts.category_id = categories.id
|
||||
INNER JOIN articles ON articles.category_id = categories.id
|
||||
```
|
||||
|
||||
Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use `Category.joins(:posts).uniq`.
|
||||
Or, in English: "return a Category object for all categories with articles". Note that you will see duplicate categories if more than one article has the same category. If you want unique categories, you can use `Category.joins(:articles).uniq`.
|
||||
|
||||
#### Joining Multiple Associations
|
||||
|
||||
```ruby
|
||||
Post.joins(:category, :comments)
|
||||
Article.joins(:category, :comments)
|
||||
```
|
||||
|
||||
This produces:
|
||||
|
||||
```sql
|
||||
SELECT posts.* FROM posts
|
||||
INNER JOIN categories ON posts.category_id = categories.id
|
||||
INNER JOIN comments ON comments.post_id = posts.id
|
||||
SELECT articles.* FROM articles
|
||||
INNER JOIN categories ON articles.category_id = categories.id
|
||||
INNER JOIN comments ON comments.article_id = articles.id
|
||||
```
|
||||
|
||||
Or, in English: "return all posts that have a category and at least one comment". Note again that posts with multiple comments will show up multiple times.
|
||||
Or, in English: "return all articles that have a category and at least one comment". Note again that articles with multiple comments will show up multiple times.
|
||||
|
||||
#### Joining Nested Associations (Single Level)
|
||||
|
||||
```ruby
|
||||
Post.joins(comments: :guest)
|
||||
Article.joins(comments: :guest)
|
||||
```
|
||||
|
||||
This produces:
|
||||
|
||||
```sql
|
||||
SELECT posts.* FROM posts
|
||||
INNER JOIN comments ON comments.post_id = posts.id
|
||||
SELECT articles.* FROM articles
|
||||
INNER JOIN comments ON comments.article_id = articles.id
|
||||
INNER JOIN guests ON guests.comment_id = comments.id
|
||||
```
|
||||
|
||||
Or, in English: "return all posts that have a comment made by a guest."
|
||||
Or, in English: "return all articles that have a comment made by a guest."
|
||||
|
||||
#### Joining Nested Associations (Multiple Level)
|
||||
|
||||
```ruby
|
||||
Category.joins(posts: [{ comments: :guest }, :tags])
|
||||
Category.joins(articles: [{ comments: :guest }, :tags])
|
||||
```
|
||||
|
||||
This produces:
|
||||
|
||||
```sql
|
||||
SELECT categories.* FROM categories
|
||||
INNER JOIN posts ON posts.category_id = categories.id
|
||||
INNER JOIN comments ON comments.post_id = posts.id
|
||||
INNER JOIN articles ON articles.category_id = categories.id
|
||||
INNER JOIN comments ON comments.article_id = articles.id
|
||||
INNER JOIN guests ON guests.comment_id = comments.id
|
||||
INNER JOIN tags ON tags.post_id = posts.id
|
||||
INNER JOIN tags ON tags.article_id = articles.id
|
||||
```
|
||||
|
||||
### Specifying Conditions on the Joined Tables
|
||||
|
@ -1121,18 +1121,18 @@ Active Record lets you eager load any number of associations with a single `Mode
|
|||
#### Array of Multiple Associations
|
||||
|
||||
```ruby
|
||||
Post.includes(:category, :comments)
|
||||
Article.includes(:category, :comments)
|
||||
```
|
||||
|
||||
This loads all the posts and the associated category and comments for each post.
|
||||
This loads all the articles and the associated category and comments for each article.
|
||||
|
||||
#### Nested Associations Hash
|
||||
|
||||
```ruby
|
||||
Category.includes(posts: [{ comments: :guest }, :tags]).find(1)
|
||||
Category.includes(articles: [{ comments: :guest }, :tags]).find(1)
|
||||
```
|
||||
|
||||
This will find the category with id 1 and eager load all of the associated posts, the associated posts' tags and comments, and every comment's guest association.
|
||||
This will find the category with id 1 and eager load all of the associated articles, the associated articles' tags and comments, and every comment's guest association.
|
||||
|
||||
### Specifying Conditions on Eager Loaded Associations
|
||||
|
||||
|
@ -1141,18 +1141,18 @@ Even though Active Record lets you specify conditions on the eager loaded associ
|
|||
However if you must do this, you may use `where` as you would normally.
|
||||
|
||||
```ruby
|
||||
Post.includes(:comments).where("comments.visible" => true)
|
||||
Article.includes(:comments).where("comments.visible" => true)
|
||||
```
|
||||
|
||||
This would generate a query which contains a `LEFT OUTER JOIN` whereas the `joins` method would generate one using the `INNER JOIN` function instead.
|
||||
|
||||
```ruby
|
||||
SELECT "posts"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE (comments.visible = 1)
|
||||
SELECT "articles"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "articles" LEFT OUTER JOIN "comments" ON "comments"."article_id" = "articles"."id" WHERE (comments.visible = 1)
|
||||
```
|
||||
|
||||
If there was no `where` condition, this would generate the normal set of two queries.
|
||||
|
||||
If, in the case of this `includes` query, there were no comments for any posts, all the posts would still be loaded. By using `joins` (an INNER JOIN), the join conditions **must** match, otherwise no records will be returned.
|
||||
If, in the case of this `includes` query, there were no comments for any articles, all the articles would still be loaded. By using `joins` (an INNER JOIN), the join conditions **must** match, otherwise no records will be returned.
|
||||
|
||||
Scopes
|
||||
------
|
||||
|
@ -1162,7 +1162,7 @@ Scoping allows you to specify commonly-used queries which can be referenced as m
|
|||
To define a simple scope, we use the `scope` method inside the class, passing the query that we'd like to run when this scope is called:
|
||||
|
||||
```ruby
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
scope :published, -> { where(published: true) }
|
||||
end
|
||||
```
|
||||
|
@ -1170,7 +1170,7 @@ end
|
|||
This is exactly the same as defining a class method, and which you use is a matter of personal preference:
|
||||
|
||||
```ruby
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
def self.published
|
||||
where(published: true)
|
||||
end
|
||||
|
@ -1180,7 +1180,7 @@ end
|
|||
Scopes are also chainable within scopes:
|
||||
|
||||
```ruby
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
scope :published, -> { where(published: true) }
|
||||
scope :published_and_commented, -> { published.where("comments_count > 0") }
|
||||
end
|
||||
|
@ -1189,14 +1189,14 @@ end
|
|||
To call this `published` scope we can call it on either the class:
|
||||
|
||||
```ruby
|
||||
Post.published # => [published posts]
|
||||
Article.published # => [published articles]
|
||||
```
|
||||
|
||||
Or on an association consisting of `Post` objects:
|
||||
Or on an association consisting of `Article` objects:
|
||||
|
||||
```ruby
|
||||
category = Category.first
|
||||
category.posts.published # => [published posts belonging to this category]
|
||||
category.articles.published # => [published articles belonging to this category]
|
||||
```
|
||||
|
||||
### Passing in arguments
|
||||
|
@ -1204,7 +1204,7 @@ category.posts.published # => [published posts belonging to this category]
|
|||
Your scope can take arguments:
|
||||
|
||||
```ruby
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
scope :created_before, ->(time) { where("created_at < ?", time) }
|
||||
end
|
||||
```
|
||||
|
@ -1212,13 +1212,13 @@ end
|
|||
Call the scope as if it were a class method:
|
||||
|
||||
```ruby
|
||||
Post.created_before(Time.zone.now)
|
||||
Article.created_before(Time.zone.now)
|
||||
```
|
||||
|
||||
However, this is just duplicating the functionality that would be provided to you by a class method.
|
||||
|
||||
```ruby
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
def self.created_before(time)
|
||||
where("created_at < ?", time)
|
||||
end
|
||||
|
@ -1228,7 +1228,7 @@ end
|
|||
Using a class method is the preferred way to accept arguments for scopes. These methods will still be accessible on the association objects:
|
||||
|
||||
```ruby
|
||||
category.posts.created_before(time)
|
||||
category.articles.created_before(time)
|
||||
```
|
||||
|
||||
### Applying a default scope
|
||||
|
@ -1591,20 +1591,20 @@ You can also use `any?` and `many?` to check for existence on a model or relatio
|
|||
|
||||
```ruby
|
||||
# via a model
|
||||
Post.any?
|
||||
Post.many?
|
||||
Article.any?
|
||||
Article.many?
|
||||
|
||||
# via a named scope
|
||||
Post.recent.any?
|
||||
Post.recent.many?
|
||||
Article.recent.any?
|
||||
Article.recent.many?
|
||||
|
||||
# via a relation
|
||||
Post.where(published: true).any?
|
||||
Post.where(published: true).many?
|
||||
Article.where(published: true).any?
|
||||
Article.where(published: true).many?
|
||||
|
||||
# via an association
|
||||
Post.first.categories.any?
|
||||
Post.first.categories.many?
|
||||
Article.first.categories.any?
|
||||
Article.first.categories.many?
|
||||
```
|
||||
|
||||
Calculations
|
||||
|
@ -1694,18 +1694,18 @@ Running EXPLAIN
|
|||
You can run EXPLAIN on the queries triggered by relations. For example,
|
||||
|
||||
```ruby
|
||||
User.where(id: 1).joins(:posts).explain
|
||||
User.where(id: 1).joins(:articles).explain
|
||||
```
|
||||
|
||||
may yield
|
||||
|
||||
```
|
||||
EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `posts` ON `posts`.`user_id` = `users`.`id` WHERE `users`.`id` = 1
|
||||
EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `articles` ON `articles`.`user_id` = `users`.`id` WHERE `users`.`id` = 1
|
||||
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
||||
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|
||||
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
||||
| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
|
||||
| 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
|
||||
| 1 | SIMPLE | articles | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
|
||||
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
||||
2 rows in set (0.00 sec)
|
||||
```
|
||||
|
@ -1716,15 +1716,15 @@ Active Record performs a pretty printing that emulates the one of the database
|
|||
shells. So, the same query running with the PostgreSQL adapter would yield instead
|
||||
|
||||
```
|
||||
EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE "users"."id" = 1
|
||||
EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "articles" ON "articles"."user_id" = "users"."id" WHERE "users"."id" = 1
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------------
|
||||
Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
|
||||
Join Filter: (posts.user_id = users.id)
|
||||
Join Filter: (articles.user_id = users.id)
|
||||
-> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
|
||||
Index Cond: (id = 1)
|
||||
-> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
|
||||
Filter: (posts.user_id = 1)
|
||||
-> Seq Scan on articles (cost=0.00..28.88 rows=8 width=4)
|
||||
Filter: (articles.user_id = 1)
|
||||
(6 rows)
|
||||
```
|
||||
|
||||
|
@ -1733,7 +1733,7 @@ may need the results of previous ones. Because of that, `explain` actually
|
|||
executes the query, and then asks for the query plans. For example,
|
||||
|
||||
```ruby
|
||||
User.where(id: 1).includes(:posts).explain
|
||||
User.where(id: 1).includes(:articles).explain
|
||||
```
|
||||
|
||||
yields
|
||||
|
@ -1747,11 +1747,11 @@ EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`id` = 1
|
|||
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
|
||||
1 row in set (0.00 sec)
|
||||
|
||||
EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1)
|
||||
EXPLAIN for: SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` IN (1)
|
||||
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|
||||
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|
||||
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|
||||
| 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
|
||||
| 1 | SIMPLE | articles | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
|
||||
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|
||||
1 row in set (0.00 sec)
|
||||
```
|
||||
|
|
|
@ -1129,15 +1129,15 @@ generating a scaffold, Rails will put some ERB into the `_form.html.erb` that
|
|||
it generates that displays the full list of errors on that model.
|
||||
|
||||
Assuming we have a model that's been saved in an instance variable named
|
||||
`@post`, it looks like this:
|
||||
`@article`, it looks like this:
|
||||
|
||||
```ruby
|
||||
<% if @post.errors.any? %>
|
||||
<% if @article.errors.any? %>
|
||||
<div id="error_explanation">
|
||||
<h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
|
||||
<h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved:</h2>
|
||||
|
||||
<ul>
|
||||
<% @post.errors.full_messages.each do |msg| %>
|
||||
<% @article.errors.full_messages.each do |msg| %>
|
||||
<li><%= msg %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
@ -1151,7 +1151,7 @@ the entry.
|
|||
|
||||
```
|
||||
<div class="field_with_errors">
|
||||
<input id="post_title" name="post[title]" size="30" type="text" value="">
|
||||
<input id="article_title" name="article[title]" size="30" type="text" value="">
|
||||
</div>
|
||||
```
|
||||
|
||||
|
|
|
@ -3838,7 +3838,7 @@ The name may be given as a symbol or string. A symbol is tested against the bare
|
|||
|
||||
TIP: A symbol can represent a fully-qualified constant name as in `:"ActiveRecord::Base"`, so the behavior for symbols is defined for convenience, not because it has to be that way technically.
|
||||
|
||||
For example, when an action of `PostsController` is called Rails tries optimistically to use `PostsHelper`. It is OK that the helper module does not exist, so if an exception for that constant name is raised it should be silenced. But it could be the case that `posts_helper.rb` raises a `NameError` due to an actual unknown constant. That should be reraised. The method `missing_name?` provides a way to distinguish both cases:
|
||||
For example, when an action of `ArticlesController` is called Rails tries optimistically to use `ArticlesHelper`. It is OK that the helper module does not exist, so if an exception for that constant name is raised it should be silenced. But it could be the case that `articles_helper.rb` raises a `NameError` due to an actual unknown constant. That should be reraised. The method `missing_name?` provides a way to distinguish both cases:
|
||||
|
||||
```ruby
|
||||
def default_helper_module!
|
||||
|
@ -3861,7 +3861,7 @@ Active Support adds `is_missing?` to `LoadError`, and also assigns that class to
|
|||
|
||||
Given a path name `is_missing?` tests whether the exception was raised due to that particular file (except perhaps for the ".rb" extension).
|
||||
|
||||
For example, when an action of `PostsController` is called Rails tries to load `posts_helper.rb`, but that file may not exist. That's fine, the helper module is not mandatory so Rails silences a load error. But it could be the case that the helper module does exist and in turn requires another library that is missing. In that case Rails must reraise the exception. The method `is_missing?` provides a way to distinguish both cases:
|
||||
For example, when an action of `ArticlesController` is called Rails tries to load `articles_helper.rb`, but that file may not exist. That's fine, the helper module is not mandatory so Rails silences a load error. But it could be the case that the helper module does exist and in turn requires another library that is missing. In that case Rails must reraise the exception. The method `is_missing?` provides a way to distinguish both cases:
|
||||
|
||||
```ruby
|
||||
def default_helper_module!
|
||||
|
|
|
@ -110,14 +110,14 @@ The results of expressions follow them and are introduced by "# => ", vertically
|
|||
If a line is too long, the comment may be placed on the next line:
|
||||
|
||||
```ruby
|
||||
# label(:post, :title)
|
||||
# # => <label for="post_title">Title</label>
|
||||
# label(:article, :title)
|
||||
# # => <label for="article_title">Title</label>
|
||||
#
|
||||
# label(:post, :title, "A short title")
|
||||
# # => <label for="post_title">A short title</label>
|
||||
# label(:article, :title, "A short title")
|
||||
# # => <label for="article_title">A short title</label>
|
||||
#
|
||||
# label(:post, :title, "A short title", class: "title_label")
|
||||
# # => <label for="post_title" class="title_label">A short title</label>
|
||||
# label(:article, :title, "A short title", class: "title_label")
|
||||
# # => <label for="article_title" class="title_label">A short title</label>
|
||||
```
|
||||
|
||||
Avoid using any printing methods like `puts` or `p` for that purpose.
|
||||
|
|
|
@ -1725,58 +1725,58 @@ mostly useful together with the `:through` option.
|
|||
```ruby
|
||||
class Person < ActiveRecord::Base
|
||||
has_many :readings
|
||||
has_many :posts, through: :readings
|
||||
has_many :articles, through: :readings
|
||||
end
|
||||
|
||||
person = Person.create(name: 'John')
|
||||
post = Post.create(name: 'a1')
|
||||
person.posts << post
|
||||
person.posts << post
|
||||
person.posts.inspect # => [#<Post id: 5, name: "a1">, #<Post id: 5, name: "a1">]
|
||||
Reading.all.inspect # => [#<Reading id: 12, person_id: 5, post_id: 5>, #<Reading id: 13, person_id: 5, post_id: 5>]
|
||||
article = Article.create(name: 'a1')
|
||||
person.articles << article
|
||||
person.articles << article
|
||||
person.articles.inspect # => [#<Article id: 5, name: "a1">, #<Article id: 5, name: "a1">]
|
||||
Reading.all.inspect # => [#<Reading id: 12, person_id: 5, article_id: 5>, #<Reading id: 13, person_id: 5, article_id: 5>]
|
||||
```
|
||||
|
||||
In the above case there are two readings and `person.posts` brings out both of
|
||||
them even though these records are pointing to the same post.
|
||||
In the above case there are two readings and `person.articles` brings out both of
|
||||
them even though these records are pointing to the same article.
|
||||
|
||||
Now let's set `distinct`:
|
||||
|
||||
```ruby
|
||||
class Person
|
||||
has_many :readings
|
||||
has_many :posts, -> { distinct }, through: :readings
|
||||
has_many :articles, -> { distinct }, through: :readings
|
||||
end
|
||||
|
||||
person = Person.create(name: 'Honda')
|
||||
post = Post.create(name: 'a1')
|
||||
person.posts << post
|
||||
person.posts << post
|
||||
person.posts.inspect # => [#<Post id: 7, name: "a1">]
|
||||
Reading.all.inspect # => [#<Reading id: 16, person_id: 7, post_id: 7>, #<Reading id: 17, person_id: 7, post_id: 7>]
|
||||
article = Article.create(name: 'a1')
|
||||
person.articles << article
|
||||
person.articles << article
|
||||
person.articles.inspect # => [#<Article id: 7, name: "a1">]
|
||||
Reading.all.inspect # => [#<Reading id: 16, person_id: 7, article_id: 7>, #<Reading id: 17, person_id: 7, article_id: 7>]
|
||||
```
|
||||
|
||||
In the above case there are still two readings. However `person.posts` shows
|
||||
only one post because the collection loads only unique records.
|
||||
In the above case there are still two readings. However `person.articles` shows
|
||||
only one article because the collection loads only unique records.
|
||||
|
||||
If you want to make sure that, upon insertion, all of the records in the
|
||||
persisted association are distinct (so that you can be sure that when you
|
||||
inspect the association that you will never find duplicate records), you should
|
||||
add a unique index on the table itself. For example, if you have a table named
|
||||
`person_posts` and you want to make sure all the posts are unique, you could
|
||||
`person_articles` and you want to make sure all the articles are unique, you could
|
||||
add the following in a migration:
|
||||
|
||||
```ruby
|
||||
add_index :person_posts, :post, unique: true
|
||||
add_index :person_articles, :article, unique: true
|
||||
```
|
||||
|
||||
Note that checking for uniqueness using something like `include?` is subject
|
||||
to race conditions. Do not attempt to use `include?` to enforce distinctness
|
||||
in an association. For instance, using the post example from above, the
|
||||
in an association. For instance, using the article example from above, the
|
||||
following code would be racy because multiple users could be attempting this
|
||||
at the same time:
|
||||
|
||||
```ruby
|
||||
person.posts << post unless person.posts.include?(post)
|
||||
person.articles << article unless person.articles.include?(article)
|
||||
```
|
||||
|
||||
#### When are Objects Saved?
|
||||
|
|
|
@ -454,7 +454,7 @@ You can also use custom annotations in your code and list them using `rake notes
|
|||
```bash
|
||||
$ bin/rake notes:custom ANNOTATION=BUG
|
||||
(in /home/foobar/commandsapp)
|
||||
app/models/post.rb:
|
||||
app/models/article.rb:
|
||||
* [ 23] Have to fix this one before pushing!
|
||||
```
|
||||
|
||||
|
|
|
@ -388,13 +388,13 @@ encrypted cookies salt value. Defaults to `'signed encrypted cookie'`.
|
|||
|
||||
* `config.action_view.embed_authenticity_token_in_remote_forms` allows you to set the default behavior for `authenticity_token` in forms with `:remote => true`. By default it's set to false, which means that remote forms will not include `authenticity_token`, which is helpful when you're fragment-caching the form. Remote forms get the authenticity from the `meta` tag, so embedding is unnecessary unless you support browsers without JavaScript. In such case you can either pass `:authenticity_token => true` as a form option or set this config setting to `true`
|
||||
|
||||
* `config.action_view.prefix_partial_path_with_controller_namespace` determines whether or not partials are looked up from a subdirectory in templates rendered from namespaced controllers. For example, consider a controller named `Admin::PostsController` which renders this template:
|
||||
* `config.action_view.prefix_partial_path_with_controller_namespace` determines whether or not partials are looked up from a subdirectory in templates rendered from namespaced controllers. For example, consider a controller named `Admin::ArticlesController` which renders this template:
|
||||
|
||||
```erb
|
||||
<%= render @post %>
|
||||
<%= render @article %>
|
||||
```
|
||||
|
||||
The default setting is `true`, which uses the partial at `/admin/posts/_post.erb`. Setting the value to `false` would render `/posts/_post.erb`, which is the same behavior as rendering from a non-namespaced controller such as `PostsController`.
|
||||
The default setting is `true`, which uses the partial at `/admin/articles/_article.erb`. Setting the value to `false` would render `/articles/_article.erb`, which is the same behavior as rendering from a non-namespaced controller such as `ArticlesController`.
|
||||
|
||||
* `config.action_view.raise_on_missing_translations` determines whether an error should be raised for missing translations
|
||||
|
||||
|
|
|
@ -361,9 +361,9 @@ it should not be necessary to visit a webpage to check the history.
|
|||
Description can have multiple paragraphs and you can use code examples
|
||||
inside, just indent it with 4 spaces:
|
||||
|
||||
class PostsController
|
||||
class ArticlesController
|
||||
def index
|
||||
respond_with Post.limit(10)
|
||||
respond_with Article.limit(10)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -26,17 +26,17 @@ One common task is to inspect the contents of a variable. In Rails, you can do t
|
|||
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:
|
||||
|
||||
```html+erb
|
||||
<%= debug @post %>
|
||||
<%= debug @article %>
|
||||
<p>
|
||||
<b>Title:</b>
|
||||
<%= @post.title %>
|
||||
<%= @article.title %>
|
||||
</p>
|
||||
```
|
||||
|
||||
You'll see something like this:
|
||||
|
||||
```yaml
|
||||
--- !ruby/object:Post
|
||||
--- !ruby/object Article
|
||||
attributes:
|
||||
updated_at: 2008-09-05 22:55:47
|
||||
body: It's a very helpful guide for debugging your Rails app.
|
||||
|
@ -55,10 +55,10 @@ Title: Rails debugging guide
|
|||
Displaying an instance variable, or any other object or method, in YAML format can be achieved this way:
|
||||
|
||||
```html+erb
|
||||
<%= simple_format @post.to_yaml %>
|
||||
<%= simple_format @article.to_yaml %>
|
||||
<p>
|
||||
<b>Title:</b>
|
||||
<%= @post.title %>
|
||||
<%= @article.title %>
|
||||
</p>
|
||||
```
|
||||
|
||||
|
@ -67,7 +67,7 @@ The `to_yaml` method converts the method to YAML format leaving it more readable
|
|||
As a result of this, you will have something like this in your view:
|
||||
|
||||
```yaml
|
||||
--- !ruby/object:Post
|
||||
--- !ruby/object Article
|
||||
attributes:
|
||||
updated_at: 2008-09-05 22:55:47
|
||||
body: It's a very helpful guide for debugging your Rails app.
|
||||
|
@ -88,7 +88,7 @@ Another useful method for displaying object values is `inspect`, especially when
|
|||
<%= [1, 2, 3, 4, 5].inspect %>
|
||||
<p>
|
||||
<b>Title:</b>
|
||||
<%= @post.title %>
|
||||
<%= @article.title %>
|
||||
</p>
|
||||
```
|
||||
|
||||
|
@ -153,18 +153,18 @@ logger.fatal "Terminating application, raised unrecoverable error!!!"
|
|||
Here's an example of a method instrumented with extra logging:
|
||||
|
||||
```ruby
|
||||
class PostsController < ApplicationController
|
||||
class ArticlesController < ApplicationController
|
||||
# ...
|
||||
|
||||
def create
|
||||
@post = Post.new(params[:post])
|
||||
logger.debug "New post: #{@post.attributes.inspect}"
|
||||
logger.debug "Post should be valid: #{@post.valid?}"
|
||||
@article = Article.new(params[:article])
|
||||
logger.debug "New article: #{@article.attributes.inspect}"
|
||||
logger.debug Article should be valid: #{@article.valid?}"
|
||||
|
||||
if @post.save
|
||||
flash[:notice] = 'Post was successfully created.'
|
||||
logger.debug "The post was saved and now the user is going to be redirected..."
|
||||
redirect_to(@post)
|
||||
if @article.save
|
||||
flash[:notice] = Article was successfully created.'
|
||||
logger.debug "The article was saved and now the user is going to be redirected..."
|
||||
redirect_to(@article)
|
||||
else
|
||||
render action: "new"
|
||||
end
|
||||
|
@ -177,21 +177,20 @@ end
|
|||
Here's an example of the log generated when this controller action is executed:
|
||||
|
||||
```
|
||||
Processing PostsController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST]
|
||||
Processing ArticlesController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST]
|
||||
Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGl
|
||||
vbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b06c4d724596a4
|
||||
Parameters: {"commit"=>"Create", "post"=>{"title"=>"Debugging Rails",
|
||||
Parameters: {"commit"=>"Create", "article"=>{"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",
|
||||
"authenticity_token"=>"2059c1286e93402e389127b1153204e0d1e275dd", "action"=>"create", "controller"=>"articles"}
|
||||
New article: {"updated_at"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!",
|
||||
"published"=>false, "created_at"=>nil} Article should be valid: true
|
||||
Article Create (0.000443) INSERT INTO "articles" ("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 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]
|
||||
The article was saved and now the user is going to be redirected...
|
||||
Redirected to # Article:0x20af760>
|
||||
Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/articles]
|
||||
```
|
||||
|
||||
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.
|
||||
|
@ -286,17 +285,17 @@ Before the prompt, the code around the line that is about to be run will be
|
|||
displayed and the current line will be marked by '=>'. Like this:
|
||||
|
||||
```
|
||||
[1, 10] in /PathTo/project/app/controllers/posts_controller.rb
|
||||
[1, 10] in /PathTo/project/app/controllers/articles_controller.rb
|
||||
3:
|
||||
4: # GET /posts
|
||||
5: # GET /posts.json
|
||||
4: # GET /articles
|
||||
5: # GET /articles.json
|
||||
6: def index
|
||||
7: byebug
|
||||
=> 8: @posts = Post.find_recent
|
||||
=> 8: @articles = Article.find_recent
|
||||
9:
|
||||
10: respond_to do |format|
|
||||
11: format.html # index.html.erb
|
||||
12: format.json { render json: @posts }
|
||||
12: format.json { render json: @articles }
|
||||
|
||||
(byebug)
|
||||
```
|
||||
|
@ -320,19 +319,19 @@ For example:
|
|||
|
||||
Started GET "/" for 127.0.0.1 at 2014-04-11 13:11:48 +0200
|
||||
ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations".* FROM "schema_migrations"
|
||||
Processing by PostsController#index as HTML
|
||||
Processing by ArticlesController#index as HTML
|
||||
|
||||
[3, 12] in /PathTo/project/app/controllers/posts_controller.rb
|
||||
[3, 12] in /PathTo/project/app/controllers/articles_controller.rb
|
||||
3:
|
||||
4: # GET /posts
|
||||
5: # GET /posts.json
|
||||
4: # GET /articles
|
||||
5: # GET /articles.json
|
||||
6: def index
|
||||
7: byebug
|
||||
=> 8: @posts = Post.find_recent
|
||||
=> 8: @articles = Article.find_recent
|
||||
9:
|
||||
10: respond_to do |format|
|
||||
11: format.html # index.html.erb
|
||||
12: format.json { render json: @posts }
|
||||
12: format.json { render json: @articles }
|
||||
|
||||
(byebug)
|
||||
```
|
||||
|
@ -365,15 +364,15 @@ To see the previous ten lines you should type `list-` (or `l-`)
|
|||
```
|
||||
(byebug) l-
|
||||
|
||||
[1, 10] in /PathTo/project/app/controllers/posts_controller.rb
|
||||
1 class PostsController < ApplicationController
|
||||
2 before_action :set_post, only: [:show, :edit, :update, :destroy]
|
||||
[1, 10] in /PathTo/project/app/controllers/articles_controller.rb
|
||||
1 class ArticlesController < ApplicationController
|
||||
2 before_action :set_article, only: [:show, :edit, :update, :destroy]
|
||||
3
|
||||
4 # GET /posts
|
||||
5 # GET /posts.json
|
||||
4 # GET /articles
|
||||
5 # GET /articles.json
|
||||
6 def index
|
||||
7 byebug
|
||||
8 @posts = Post.find_recent
|
||||
8 @articles = Article.find_recent
|
||||
9
|
||||
10 respond_to do |format|
|
||||
|
||||
|
@ -386,17 +385,17 @@ the code again you can type `list=`
|
|||
```
|
||||
(byebug) list=
|
||||
|
||||
[3, 12] in /PathTo/project/app/controllers/posts_controller.rb
|
||||
[3, 12] in /PathTo/project/app/controllers/articles_controller.rb
|
||||
3:
|
||||
4: # GET /posts
|
||||
5: # GET /posts.json
|
||||
4: # GET /articles
|
||||
5: # GET /articles.json
|
||||
6: def index
|
||||
7: byebug
|
||||
=> 8: @posts = Post.find_recent
|
||||
=> 8: @articles = Article.find_recent
|
||||
9:
|
||||
10: respond_to do |format|
|
||||
11: format.html # index.html.erb
|
||||
12: format.json { render json: @posts }
|
||||
12: format.json { render json: @articles }
|
||||
|
||||
(byebug)
|
||||
```
|
||||
|
@ -419,8 +418,8 @@ then `backtrace` will supply the answer.
|
|||
|
||||
```
|
||||
(byebug) where
|
||||
--> #0 PostsController.index
|
||||
at /PathTo/project/test_app/app/controllers/posts_controller.rb:8
|
||||
--> #0 ArticlesController.index
|
||||
at /PathTo/project/test_app/app/controllers/articles_controller.rb:8
|
||||
#1 ActionController::ImplicitRender.send_action(method#String, *args#Array)
|
||||
at /PathToGems/actionpack-4.1.0/lib/action_controller/metal/implicit_render.rb:4
|
||||
#2 AbstractController::Base.process_action(action#NilClass, *args#Array)
|
||||
|
@ -487,17 +486,17 @@ This example shows how you can print the instance variables defined within the
|
|||
current context:
|
||||
|
||||
```
|
||||
[3, 12] in /PathTo/project/app/controllers/posts_controller.rb
|
||||
[3, 12] in /PathTo/project/app/controllers/articles_controller.rb
|
||||
3:
|
||||
4: # GET /posts
|
||||
5: # GET /posts.json
|
||||
4: # GET /articles
|
||||
5: # GET /articles.json
|
||||
6: def index
|
||||
7: byebug
|
||||
=> 8: @posts = Post.find_recent
|
||||
=> 8: @articles = Article.find_recent
|
||||
9:
|
||||
10: respond_to do |format|
|
||||
11: format.html # index.html.erb
|
||||
12: format.json { render json: @posts }
|
||||
12: format.json { render json: @articles }
|
||||
|
||||
(byebug) instance_variables
|
||||
[:@_action_has_layout, :@_routes, :@_headers, :@_status, :@_request,
|
||||
|
@ -512,15 +511,15 @@ command later in this guide).
|
|||
|
||||
```
|
||||
(byebug) next
|
||||
[5, 14] in /PathTo/project/app/controllers/posts_controller.rb
|
||||
5 # GET /posts.json
|
||||
[5, 14] in /PathTo/project/app/controllers/articles_controller.rb
|
||||
5 # GET /articles.json
|
||||
6 def index
|
||||
7 byebug
|
||||
8 @posts = Post.find_recent
|
||||
8 @articles = Article.find_recent
|
||||
9
|
||||
=> 10 respond_to do |format|
|
||||
11 format.html # index.html.erb
|
||||
12 format.json { render json: @posts }
|
||||
12 format.json { render json: @articles }
|
||||
13 end
|
||||
14 end
|
||||
15
|
||||
|
@ -530,11 +529,11 @@ command later in this guide).
|
|||
And then ask again for the instance_variables:
|
||||
|
||||
```
|
||||
(byebug) instance_variables.include? "@posts"
|
||||
(byebug) instance_variables.include? "@articles"
|
||||
true
|
||||
```
|
||||
|
||||
Now `@posts` is included in the instance variables, because the line defining it
|
||||
Now `@articles` is 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!).
|
||||
|
@ -564,7 +563,7 @@ example, to check that we have no local variables currently defined.
|
|||
You can also inspect for an object method this way:
|
||||
|
||||
```
|
||||
(byebug) var instance Post.new
|
||||
(byebug) var instance Article.new
|
||||
@_start_transaction_state = {}
|
||||
@aggregation_cache = {}
|
||||
@association_cache = {}
|
||||
|
@ -581,8 +580,8 @@ 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.
|
||||
|
||||
```
|
||||
(byebug) display @posts
|
||||
1: @posts = nil
|
||||
(byebug) display @articles
|
||||
1: @articles = nil
|
||||
```
|
||||
|
||||
The variables inside the displaying list will be printed with their values after
|
||||
|
@ -611,10 +610,10 @@ For example, consider the following situation:
|
|||
|
||||
```ruby
|
||||
Started GET "/" for 127.0.0.1 at 2014-04-11 13:39:23 +0200
|
||||
Processing by PostsController#index as HTML
|
||||
Processing by ArticlesController#index as HTML
|
||||
|
||||
[1, 8] in /home/davidr/Proyectos/test_app/app/models/post.rb
|
||||
1: class Post < ActiveRecord::Base
|
||||
[1, 8] in /home/davidr/Proyectos/test_app/app/models/article.rb
|
||||
1: class Article < ActiveRecord::Base
|
||||
2:
|
||||
3: def self.find_recent(limit = 10)
|
||||
4: byebug
|
||||
|
@ -634,15 +633,15 @@ the method, so `byebug` will jump to next next line of the previous frame.
|
|||
(byebug) next
|
||||
Next went up a frame because previous frame finished
|
||||
|
||||
[4, 13] in /PathTo/project/test_app/app/controllers/posts_controller.rb
|
||||
4: # GET /posts
|
||||
5: # GET /posts.json
|
||||
[4, 13] in /PathTo/project/test_app/app/controllers/articles_controller.rb
|
||||
4: # GET /articles
|
||||
5: # GET /articles.json
|
||||
6: def index
|
||||
7: @posts = Post.find_recent
|
||||
7: @articles = Article.find_recent
|
||||
8:
|
||||
=> 9: respond_to do |format|
|
||||
10: format.html # index.html.erb
|
||||
11: format.json { render json: @posts }
|
||||
11: format.json { render json: @articles }
|
||||
12: end
|
||||
13: end
|
||||
|
||||
|
@ -693,20 +692,20 @@ _expression_ works the same way as with file:line.
|
|||
For example, in the previous situation
|
||||
|
||||
```
|
||||
[4, 13] in /PathTo/project/app/controllers/posts_controller.rb
|
||||
4: # GET /posts
|
||||
5: # GET /posts.json
|
||||
[4, 13] in /PathTo/project/app/controllers/articles_controller.rb
|
||||
4: # GET /articles
|
||||
5: # GET /articles.json
|
||||
6: def index
|
||||
7: @posts = Post.find_recent
|
||||
7: @articles = Article.find_recent
|
||||
8:
|
||||
=> 9: respond_to do |format|
|
||||
10: format.html # index.html.erb
|
||||
11: format.json { render json: @posts }
|
||||
11: format.json { render json: @articles }
|
||||
12: end
|
||||
13: end
|
||||
|
||||
(byebug) break 11
|
||||
Created breakpoint 1 at /PathTo/project/app/controllers/posts_controller.rb:11
|
||||
Created breakpoint 1 at /PathTo/project/app/controllers/articles_controller.rb:11
|
||||
|
||||
```
|
||||
|
||||
|
@ -716,7 +715,7 @@ supply a number, it lists that breakpoint. Otherwise it lists all breakpoints.
|
|||
```
|
||||
(byebug) info breakpoints
|
||||
Num Enb What
|
||||
1 y at /PathTo/project/app/controllers/posts_controller.rb:11
|
||||
1 y at /PathTo/project/app/controllers/articles_controller.rb:11
|
||||
```
|
||||
|
||||
To delete breakpoints: use the command `delete _n_` to remove the breakpoint
|
||||
|
|
|
@ -36,14 +36,14 @@ engine **can** be a plugin, and a plugin **can** be an engine.
|
|||
|
||||
The engine that will be created in this guide will be called "blorgh". The
|
||||
engine will provide blogging functionality to its host applications, allowing
|
||||
for new posts and comments to be created. At the beginning of this guide, you
|
||||
for new articles and comments to be created. At the beginning of this guide, you
|
||||
will be working solely within the engine itself, but in later sections you'll
|
||||
see how to hook it into an application.
|
||||
|
||||
Engines can also be isolated from their host applications. This means that an
|
||||
application is able to have a path provided by a routing helper such as
|
||||
`posts_path` and use an engine also that provides a path also called
|
||||
`posts_path`, and the two would not clash. Along with this, controllers, models
|
||||
`articles_path` and use an engine also that provides a path also called
|
||||
`articles_path`, and the two would not clash. Along with this, controllers, models
|
||||
and table names are also namespaced. You'll see how to do this later in this
|
||||
guide.
|
||||
|
||||
|
@ -197,12 +197,12 @@ within the `Engine` class definition. Without it, classes generated in an engine
|
|||
**may** conflict with an application.
|
||||
|
||||
What this isolation of the namespace means is that a model generated by a call
|
||||
to `bin/rails g model`, such as `bin/rails g model post`, won't be called `Post`, but
|
||||
instead be namespaced and called `Blorgh::Post`. In addition, the table for the
|
||||
model is namespaced, becoming `blorgh_posts`, rather than simply `posts`.
|
||||
Similar to the model namespacing, a controller called `PostsController` becomes
|
||||
`Blorgh::PostsController` and the views for that controller will not be at
|
||||
`app/views/posts`, but `app/views/blorgh/posts` instead. Mailers are namespaced
|
||||
to `bin/rails g model`, such as `bin/rails g model article`, won't be called `Article`, but
|
||||
instead be namespaced and called `Blorgh::Article`. In addition, the table for the
|
||||
model is namespaced, becoming `blorgh_articles`, rather than simply `articles`.
|
||||
Similar to the model namespacing, a controller called `ArticlesController` becomes
|
||||
`Blorgh::ArticlesController` and the views for that controller will not be at
|
||||
`app/views/articles`, but `app/views/blorgh/articles` instead. Mailers are namespaced
|
||||
as well.
|
||||
|
||||
Finally, routes will also be isolated within the engine. This is one of the most
|
||||
|
@ -283,74 +283,74 @@ created in the `test` directory as well. For example, you may wish to create a
|
|||
Providing engine functionality
|
||||
------------------------------
|
||||
|
||||
The engine that this guide covers provides posting and commenting functionality
|
||||
and follows a similar thread to the [Getting Started
|
||||
The engine that this guide covers provides submitting articles and commenting
|
||||
functionality and follows a similar thread to the [Getting Started
|
||||
Guide](getting_started.html), with some new twists.
|
||||
|
||||
### Generating a Post Resource
|
||||
### Generating an Article Resource
|
||||
|
||||
The first thing to generate for a blog engine is the `Post` model and related
|
||||
The first thing to generate for a blog engine is the `Article` model and related
|
||||
controller. To quickly generate this, you can use the Rails scaffold generator.
|
||||
|
||||
```bash
|
||||
$ bin/rails generate scaffold post title:string text:text
|
||||
$ bin/rails generate scaffold article title:string text:text
|
||||
```
|
||||
|
||||
This command will output this information:
|
||||
|
||||
```
|
||||
invoke active_record
|
||||
create db/migrate/[timestamp]_create_blorgh_posts.rb
|
||||
create app/models/blorgh/post.rb
|
||||
create db/migrate/[timestamp]_create_blorgh_articles.rb
|
||||
create app/models/blorgh/article.rb
|
||||
invoke test_unit
|
||||
create test/models/blorgh/post_test.rb
|
||||
create test/fixtures/blorgh/posts.yml
|
||||
create test/models/blorgh/article_test.rb
|
||||
create test/fixtures/blorgh/articles.yml
|
||||
invoke resource_route
|
||||
route resources :posts
|
||||
route resources :articles
|
||||
invoke scaffold_controller
|
||||
create app/controllers/blorgh/posts_controller.rb
|
||||
create app/controllers/blorgh/articles_controller.rb
|
||||
invoke erb
|
||||
create app/views/blorgh/posts
|
||||
create app/views/blorgh/posts/index.html.erb
|
||||
create app/views/blorgh/posts/edit.html.erb
|
||||
create app/views/blorgh/posts/show.html.erb
|
||||
create app/views/blorgh/posts/new.html.erb
|
||||
create app/views/blorgh/posts/_form.html.erb
|
||||
create app/views/blorgh/articles
|
||||
create app/views/blorgh/articles/index.html.erb
|
||||
create app/views/blorgh/articles/edit.html.erb
|
||||
create app/views/blorgh/articles/show.html.erb
|
||||
create app/views/blorgh/articles/new.html.erb
|
||||
create app/views/blorgh/articles/_form.html.erb
|
||||
invoke test_unit
|
||||
create test/controllers/blorgh/posts_controller_test.rb
|
||||
create test/controllers/blorgh/articles_controller_test.rb
|
||||
invoke helper
|
||||
create app/helpers/blorgh/posts_helper.rb
|
||||
create app/helpers/blorgh/articles_helper.rb
|
||||
invoke test_unit
|
||||
create test/helpers/blorgh/posts_helper_test.rb
|
||||
create test/helpers/blorgh/articles_helper_test.rb
|
||||
invoke assets
|
||||
invoke js
|
||||
create app/assets/javascripts/blorgh/posts.js
|
||||
create app/assets/javascripts/blorgh/articles.js
|
||||
invoke css
|
||||
create app/assets/stylesheets/blorgh/posts.css
|
||||
create app/assets/stylesheets/blorgh/articles.css
|
||||
invoke css
|
||||
create app/assets/stylesheets/scaffold.css
|
||||
```
|
||||
|
||||
The first thing that the scaffold generator does is invoke the `active_record`
|
||||
generator, which generates a migration and a model for the resource. Note here,
|
||||
however, that the migration is called `create_blorgh_posts` rather than the
|
||||
usual `create_posts`. This is due to the `isolate_namespace` method called in
|
||||
however, that the migration is called `create_blorgh_articles` rather than the
|
||||
usual `create_articles`. This is due to the `isolate_namespace` method called in
|
||||
the `Blorgh::Engine` class's definition. The model here is also namespaced,
|
||||
being placed at `app/models/blorgh/post.rb` rather than `app/models/post.rb` due
|
||||
being placed at `app/models/blorgh/article.rb` rather than `app/models/article.rb` due
|
||||
to the `isolate_namespace` call within the `Engine` class.
|
||||
|
||||
Next, the `test_unit` generator is invoked for this model, generating a model
|
||||
test at `test/models/blorgh/post_test.rb` (rather than
|
||||
`test/models/post_test.rb`) and a fixture at `test/fixtures/blorgh/posts.yml`
|
||||
(rather than `test/fixtures/posts.yml`).
|
||||
test at `test/models/blorgh/article_test.rb` (rather than
|
||||
`test/models/article_test.rb`) and a fixture at `test/fixtures/blorgh/articles.yml`
|
||||
(rather than `test/fixtures/articles.yml`).
|
||||
|
||||
After that, a line for the resource is inserted into the `config/routes.rb` file
|
||||
for the engine. This line is simply `resources :posts`, turning the
|
||||
for the engine. This line is simply `resources :articles`, turning the
|
||||
`config/routes.rb` file for the engine into this:
|
||||
|
||||
```ruby
|
||||
Blorgh::Engine.routes.draw do
|
||||
resources :posts
|
||||
resources :articles
|
||||
end
|
||||
```
|
||||
|
||||
|
@ -362,18 +362,18 @@ be isolated from those routes that are within the application. The
|
|||
[Routes](#routes) section of this guide describes it in detail.
|
||||
|
||||
Next, the `scaffold_controller` generator is invoked, generating a controller
|
||||
called `Blorgh::PostsController` (at
|
||||
`app/controllers/blorgh/posts_controller.rb`) and its related views at
|
||||
`app/views/blorgh/posts`. This generator also generates a test for the
|
||||
controller (`test/controllers/blorgh/posts_controller_test.rb`) and a helper
|
||||
(`app/helpers/blorgh/posts_controller.rb`).
|
||||
called `Blorgh::ArticlesController` (at
|
||||
`app/controllers/blorgh/articles_controller.rb`) and its related views at
|
||||
`app/views/blorgh/articles`. This generator also generates a test for the
|
||||
controller (`test/controllers/blorgh/articles_controller_test.rb`) and a helper
|
||||
(`app/helpers/blorgh/articles_controller.rb`).
|
||||
|
||||
Everything this generator has created is neatly namespaced. The controller's
|
||||
class is defined within the `Blorgh` module:
|
||||
|
||||
```ruby
|
||||
module Blorgh
|
||||
class PostsController < ApplicationController
|
||||
class ArticlesController < ApplicationController
|
||||
...
|
||||
end
|
||||
end
|
||||
|
@ -382,22 +382,22 @@ end
|
|||
NOTE: The `ApplicationController` class being inherited from here is the
|
||||
`Blorgh::ApplicationController`, not an application's `ApplicationController`.
|
||||
|
||||
The helper inside `app/helpers/blorgh/posts_helper.rb` is also namespaced:
|
||||
The helper inside `app/helpers/blorgh/articles_helper.rb` is also namespaced:
|
||||
|
||||
```ruby
|
||||
module Blorgh
|
||||
module PostsHelper
|
||||
module ArticlesHelper
|
||||
...
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
This helps prevent conflicts with any other engine or application that may have
|
||||
a post resource as well.
|
||||
a article resource as well.
|
||||
|
||||
Finally, the assets for this resource are generated in two files:
|
||||
`app/assets/javascripts/blorgh/posts.js` and
|
||||
`app/assets/stylesheets/blorgh/posts.css`. You'll see how to use these a little
|
||||
`app/assets/javascripts/blorgh/articles.js` and
|
||||
`app/assets/stylesheets/blorgh/articles.css`. You'll see how to use these a little
|
||||
later.
|
||||
|
||||
By default, the scaffold styling is not applied to the engine because the
|
||||
|
@ -412,46 +412,46 @@ tag of this layout:
|
|||
You can see what the engine has so far by running `rake db:migrate` at the root
|
||||
of our engine to run the migration generated by the scaffold generator, and then
|
||||
running `rails server` in `test/dummy`. When you open
|
||||
`http://localhost:3000/blorgh/posts` you will see the default scaffold that has
|
||||
`http://localhost:3000/blorgh/articles` you will see the default scaffold that has
|
||||
been generated. Click around! You've just generated your first engine's first
|
||||
functions.
|
||||
|
||||
If you'd rather play around in the console, `rails console` will also work just
|
||||
like a Rails application. Remember: the `Post` model is namespaced, so to
|
||||
reference it you must call it as `Blorgh::Post`.
|
||||
like a Rails application. Remember: the `Article` model is namespaced, so to
|
||||
reference it you must call it as `Blorgh::Article`.
|
||||
|
||||
```ruby
|
||||
>> Blorgh::Post.find(1)
|
||||
=> #<Blorgh::Post id: 1 ...>
|
||||
>> Blorgh::Article.find(1)
|
||||
=> #<Blorgh::Article id: 1 ...>
|
||||
```
|
||||
|
||||
One final thing is that the `posts` resource for this engine should be the root
|
||||
One final thing is that the `articles` resource for this engine should be the root
|
||||
of the engine. Whenever someone goes to the root path where the engine is
|
||||
mounted, they should be shown a list of posts. This can be made to happen if
|
||||
mounted, they should be shown a list of articles. This can be made to happen if
|
||||
this line is inserted into the `config/routes.rb` file inside the engine:
|
||||
|
||||
```ruby
|
||||
root to: "posts#index"
|
||||
root to: "articles#index"
|
||||
```
|
||||
|
||||
Now people will only need to go to the root of the engine to see all the posts,
|
||||
rather than visiting `/posts`. This means that instead of
|
||||
`http://localhost:3000/blorgh/posts`, you only need to go to
|
||||
Now people will only need to go to the root of the engine to see all the articles,
|
||||
rather than visiting `/articles`. This means that instead of
|
||||
`http://localhost:3000/blorgh/articles`, you only need to go to
|
||||
`http://localhost:3000/blorgh` now.
|
||||
|
||||
### Generating a Comments Resource
|
||||
|
||||
Now that the engine can create new blog posts, it only makes sense to add
|
||||
Now that the engine can create new articles, it only makes sense to add
|
||||
commenting functionality as well. To do this, you'll need to generate a comment
|
||||
model, a comment controller and then modify the posts scaffold to display
|
||||
model, a comment controller and then modify the articles scaffold to display
|
||||
comments and allow people to create new ones.
|
||||
|
||||
From the application root, run the model generator. Tell it to generate a
|
||||
`Comment` model, with the related table having two columns: a `post_id` integer
|
||||
`Comment` model, with the related table having two columns: a `article_id` integer
|
||||
and `text` text column.
|
||||
|
||||
```bash
|
||||
$ bin/rails generate model Comment post_id:integer text:text
|
||||
$ bin/rails generate model Comment article_id:integer text:text
|
||||
```
|
||||
|
||||
This will output the following:
|
||||
|
@ -474,17 +474,17 @@ table:
|
|||
$ bin/rake db:migrate
|
||||
```
|
||||
|
||||
To show the comments on a post, edit `app/views/blorgh/posts/show.html.erb` and
|
||||
To show the comments on an article, edit `app/views/blorgh/articles/show.html.erb` and
|
||||
add this line before the "Edit" link:
|
||||
|
||||
```html+erb
|
||||
<h3>Comments</h3>
|
||||
<%= render @post.comments %>
|
||||
<%= render @article.comments %>
|
||||
```
|
||||
|
||||
This line will require there to be a `has_many` association for comments defined
|
||||
on the `Blorgh::Post` model, which there isn't right now. To define one, open
|
||||
`app/models/blorgh/post.rb` and add this line into the model:
|
||||
on the `Blorgh::Article` model, which there isn't right now. To define one, open
|
||||
`app/models/blorgh/article.rb` and add this line into the model:
|
||||
|
||||
```ruby
|
||||
has_many :comments
|
||||
|
@ -494,7 +494,7 @@ Turning the model into this:
|
|||
|
||||
```ruby
|
||||
module Blorgh
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
has_many :comments
|
||||
end
|
||||
end
|
||||
|
@ -505,9 +505,9 @@ NOTE: Because the `has_many` is defined inside a class that is inside the
|
|||
model for these objects, so there's no need to specify that using the
|
||||
`:class_name` option here.
|
||||
|
||||
Next, there needs to be a form so that comments can be created on a post. To add
|
||||
this, put this line underneath the call to `render @post.comments` in
|
||||
`app/views/blorgh/posts/show.html.erb`:
|
||||
Next, there needs to be a form so that comments can be created on a article. To add
|
||||
this, put this line underneath the call to `render @article.comments` in
|
||||
`app/views/blorgh/articles/show.html.erb`:
|
||||
|
||||
```erb
|
||||
<%= render "blorgh/comments/form" %>
|
||||
|
@ -519,7 +519,7 @@ directory at `app/views/blorgh/comments` and in it a new file called
|
|||
|
||||
```html+erb
|
||||
<h3>New comment</h3>
|
||||
<%= form_for [@post, @post.comments.build] do |f| %>
|
||||
<%= form_for [@article, @article.comments.build] do |f| %>
|
||||
<p>
|
||||
<%= f.label :text %><br>
|
||||
<%= f.text_area :text %>
|
||||
|
@ -529,12 +529,12 @@ directory at `app/views/blorgh/comments` and in it a new file called
|
|||
```
|
||||
|
||||
When this form is submitted, it is going to attempt to perform a `POST` request
|
||||
to a route of `/posts/:post_id/comments` within the engine. This route doesn't
|
||||
exist at the moment, but can be created by changing the `resources :posts` line
|
||||
to a route of `/articles/:article_id/comments` within the engine. This route doesn't
|
||||
exist at the moment, but can be created by changing the `resources :articles` line
|
||||
inside `config/routes.rb` into these lines:
|
||||
|
||||
```ruby
|
||||
resources :posts do
|
||||
resources :articles do
|
||||
resources :comments
|
||||
end
|
||||
```
|
||||
|
@ -567,17 +567,17 @@ invoke css
|
|||
create app/assets/stylesheets/blorgh/comments.css
|
||||
```
|
||||
|
||||
The form will be making a `POST` request to `/posts/:post_id/comments`, which
|
||||
The form will be making a `POST` request to `/articles/:article_id/comments`, which
|
||||
will correspond with the `create` action in `Blorgh::CommentsController`. This
|
||||
action needs to be created, which can be done by putting the following lines
|
||||
inside the class definition in `app/controllers/blorgh/comments_controller.rb`:
|
||||
|
||||
```ruby
|
||||
def create
|
||||
@post = Post.find(params[:post_id])
|
||||
@comment = @post.comments.create(comment_params)
|
||||
@article = Article.find(params[:article_id])
|
||||
@comment = @article.comments.create(comment_params)
|
||||
flash[:notice] = "Comment has been created!"
|
||||
redirect_to posts_path
|
||||
redirect_to articles_path
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -590,11 +590,11 @@ This is the final step required to get the new comment form working. Displaying
|
|||
the comments, however, is not quite right yet. If you were to create a comment
|
||||
right now, you would see this error:
|
||||
|
||||
```
|
||||
```
|
||||
Missing partial blorgh/comments/comment with {:handlers=>[:erb, :builder],
|
||||
:formats=>[:html], :locale=>[:en, :en]}. Searched in: *
|
||||
"/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views" *
|
||||
"/Users/ryan/Sites/side_projects/blorgh/app/views"
|
||||
"/Users/ryan/Sites/side_projects/blorgh/app/views"
|
||||
```
|
||||
|
||||
The engine is unable to find the partial required for rendering the comments.
|
||||
|
@ -612,7 +612,7 @@ line inside it:
|
|||
```
|
||||
|
||||
The `comment_counter` local variable is given to us by the `<%= render
|
||||
@post.comments %>` call, which will define it automatically and increment the
|
||||
@article.comments %>` call, which will define it automatically and increment the
|
||||
counter as it iterates through each comment. It's used in this example to
|
||||
display a small number next to each comment when it's created.
|
||||
|
||||
|
@ -625,7 +625,7 @@ Hooking Into an Application
|
|||
Using an engine within an application is very easy. This section covers how to
|
||||
mount the engine into an application and the initial setup required, as well as
|
||||
linking the engine to a `User` class provided by the application to provide
|
||||
ownership for posts and comments within the engine.
|
||||
ownership for articles and comments within the engine.
|
||||
|
||||
### Mounting the Engine
|
||||
|
||||
|
@ -676,7 +676,7 @@ pre-defined path which may be customizable.
|
|||
|
||||
### Engine setup
|
||||
|
||||
The engine contains migrations for the `blorgh_posts` and `blorgh_comments`
|
||||
The engine contains migrations for the `blorgh_articles` and `blorgh_comments`
|
||||
table which need to be created in the application's database so that the
|
||||
engine's models can query them correctly. To copy these migrations into the
|
||||
application use this command:
|
||||
|
@ -698,7 +698,7 @@ haven't been copied over already. The first run for this command will output
|
|||
something such as this:
|
||||
|
||||
```bash
|
||||
Copied migration [timestamp_1]_create_blorgh_posts.rb from blorgh
|
||||
Copied migration [timestamp_1]_create_blorgh_articles.rb from blorgh
|
||||
Copied migration [timestamp_2]_create_blorgh_comments.rb from blorgh
|
||||
```
|
||||
|
||||
|
@ -709,7 +709,7 @@ migrations in the application.
|
|||
|
||||
To run these migrations within the context of the application, simply run `rake
|
||||
db:migrate`. When accessing the engine through `http://localhost:3000/blog`, the
|
||||
posts will be empty. This is because the table created inside the application is
|
||||
articles will be empty. This is because the table created inside the application is
|
||||
different from the one created within the engine. Go ahead, play around with the
|
||||
newly mounted engine. You'll find that it's the same as when it was only an
|
||||
engine.
|
||||
|
@ -734,11 +734,11 @@ rake db:migrate SCOPE=blorgh VERSION=0
|
|||
|
||||
When an engine is created, it may want to use specific classes from an
|
||||
application to provide links between the pieces of the engine and the pieces of
|
||||
the application. In the case of the `blorgh` engine, making posts and comments
|
||||
the application. In the case of the `blorgh` engine, making articles and comments
|
||||
have authors would make a lot of sense.
|
||||
|
||||
A typical application might have a `User` class that would be used to represent
|
||||
authors for a post or a comment. But there could be a case where the application
|
||||
authors for a article or a comment. But there could be a case where the application
|
||||
calls this class something different, such as `Person`. For this reason, the
|
||||
engine should not hardcode associations specifically for a `User` class.
|
||||
|
||||
|
@ -753,14 +753,14 @@ rails g model user name:string
|
|||
The `rake db:migrate` command needs to be run here to ensure that our
|
||||
application has the `users` table for future use.
|
||||
|
||||
Also, to keep it simple, the posts form will have a new text field called
|
||||
Also, to keep it simple, the articles form will have a new text field called
|
||||
`author_name`, where users can elect to put their name. The engine will then
|
||||
take this name and either create a new `User` object from it, or find one that
|
||||
already has that name. The engine will then associate the post with the found or
|
||||
already has that name. The engine will then associate the article with the found or
|
||||
created `User` object.
|
||||
|
||||
First, the `author_name` text field needs to be added to the
|
||||
`app/views/blorgh/posts/_form.html.erb` partial inside the engine. This can be
|
||||
`app/views/blorgh/articles/_form.html.erb` partial inside the engine. This can be
|
||||
added above the `title` field with this code:
|
||||
|
||||
```html+erb
|
||||
|
@ -770,23 +770,23 @@ added above the `title` field with this code:
|
|||
</div>
|
||||
```
|
||||
|
||||
Next, we need to update our `Blorgh::PostController#post_params` method to
|
||||
Next, we need to update our `Blorgh::ArticleController#article_params` method to
|
||||
permit the new form parameter:
|
||||
|
||||
```ruby
|
||||
def post_params
|
||||
params.require(:post).permit(:title, :text, :author_name)
|
||||
def article_params
|
||||
params.require(:article).permit(:title, :text, :author_name)
|
||||
end
|
||||
```
|
||||
|
||||
The `Blorgh::Post` model should then have some code to convert the `author_name`
|
||||
field into an actual `User` object and associate it as that post's `author`
|
||||
before the post is saved. It will also need to have an `attr_accessor` set up
|
||||
The `Blorgh::Article` model should then have some code to convert the `author_name`
|
||||
field into an actual `User` object and associate it as that article's `author`
|
||||
before the article is saved. It will also need to have an `attr_accessor` set up
|
||||
for this field, so that the setter and getter methods are defined for it.
|
||||
|
||||
To do all this, you'll need to add the `attr_accessor` for `author_name`, the
|
||||
association for the author and the `before_save` call into
|
||||
`app/models/blorgh/post.rb`. The `author` association will be hard-coded to the
|
||||
`app/models/blorgh/article.rb`. The `author` association will be hard-coded to the
|
||||
`User` class for the time being.
|
||||
|
||||
```ruby
|
||||
|
@ -803,14 +803,14 @@ private
|
|||
|
||||
By representing the `author` association's object with the `User` class, a link
|
||||
is established between the engine and the application. There needs to be a way
|
||||
of associating the records in the `blorgh_posts` table with the records in the
|
||||
of associating the records in the `blorgh_articles` table with the records in the
|
||||
`users` table. Because the association is called `author`, there should be an
|
||||
`author_id` column added to the `blorgh_posts` table.
|
||||
`author_id` column added to the `blorgh_articles` table.
|
||||
|
||||
To generate this new column, run this command within the engine:
|
||||
|
||||
```bash
|
||||
$ bin/rails g migration add_author_id_to_blorgh_posts author_id:integer
|
||||
$ bin/rails g migration add_author_id_to_blorgh_articles author_id:integer
|
||||
```
|
||||
|
||||
NOTE: Due to the migration's name and the column specification after it, Rails
|
||||
|
@ -828,12 +828,12 @@ $ bin/rake blorgh:install:migrations
|
|||
Notice that only _one_ migration was copied over here. This is because the first
|
||||
two migrations were copied over the first time this command was run.
|
||||
|
||||
```
|
||||
NOTE Migration [timestamp]_create_blorgh_posts.rb from blorgh has been
|
||||
```
|
||||
NOTE Migration [timestamp]_create_blorgh_articles.rb from blorgh has been
|
||||
skipped. Migration with the same name already exists. NOTE Migration
|
||||
[timestamp]_create_blorgh_comments.rb from blorgh has been skipped. Migration
|
||||
with the same name already exists. Copied migration
|
||||
[timestamp]_add_author_id_to_blorgh_posts.rb from blorgh
|
||||
[timestamp]_add_author_id_to_blorgh_articles.rb from blorgh
|
||||
```
|
||||
|
||||
Run the migration using:
|
||||
|
@ -843,20 +843,20 @@ $ bin/rake db:migrate
|
|||
```
|
||||
|
||||
Now with all the pieces in place, an action will take place that will associate
|
||||
an author - represented by a record in the `users` table - with a post,
|
||||
represented by the `blorgh_posts` table from the engine.
|
||||
an author - represented by a record in the `users` table - with an article,
|
||||
represented by the `blorgh_articles` table from the engine.
|
||||
|
||||
Finally, the author's name should be displayed on the post's page. Add this code
|
||||
above the "Title" output inside `app/views/blorgh/posts/show.html.erb`:
|
||||
Finally, the author's name should be displayed on the article's page. Add this code
|
||||
above the "Title" output inside `app/views/blorgh/articles/show.html.erb`:
|
||||
|
||||
```html+erb
|
||||
<p>
|
||||
<b>Author:</b>
|
||||
<%= @post.author %>
|
||||
<%= @article.author %>
|
||||
</p>
|
||||
```
|
||||
|
||||
By outputting `@post.author` using the `<%=` tag, the `to_s` method will be
|
||||
By outputting `@article.author` using the `<%=` tag, the `to_s` method will be
|
||||
called on the object. By default, this will look quite ugly:
|
||||
|
||||
```
|
||||
|
@ -925,15 +925,15 @@ This method works like its brothers, `attr_accessor` and `cattr_accessor`, but
|
|||
provides a setter and getter method on the module with the specified name. To
|
||||
use it, it must be referenced using `Blorgh.author_class`.
|
||||
|
||||
The next step is to switch the `Blorgh::Post` model over to this new setting.
|
||||
The next step is to switch the `Blorgh::Article` model over to this new setting.
|
||||
Change the `belongs_to` association inside this model
|
||||
(`app/models/blorgh/post.rb`) to this:
|
||||
(`app/models/blorgh/article.rb`) to this:
|
||||
|
||||
```ruby
|
||||
belongs_to :author, class_name: Blorgh.author_class
|
||||
```
|
||||
|
||||
The `set_author` method in the `Blorgh::Post` model should also use this class:
|
||||
The `set_author` method in the `Blorgh::Article` model should also use this class:
|
||||
|
||||
```ruby
|
||||
self.author = Blorgh.author_class.constantize.find_or_create_by(name: author_name)
|
||||
|
@ -960,7 +960,7 @@ Resulting in something a little shorter, and more implicit in its behavior. The
|
|||
`author_class` method should always return a `Class` object.
|
||||
|
||||
Since we changed the `author_class` method to return a `Class` instead of a
|
||||
`String`, we must also modify our `belongs_to` definition in the `Blorgh::Post`
|
||||
`String`, we must also modify our `belongs_to` definition in the `Blorgh::Article`
|
||||
model:
|
||||
|
||||
```ruby
|
||||
|
@ -985,14 +985,14 @@ to load that class and then reference the related table. This could lead to
|
|||
problems if the table wasn't already existing. Therefore, a `String` should be
|
||||
used and then converted to a class using `constantize` in the engine later on.
|
||||
|
||||
Go ahead and try to create a new post. You will see that it works exactly in the
|
||||
Go ahead and try to create a new article. You will see that it works exactly in the
|
||||
same way as before, except this time the engine is using the configuration
|
||||
setting in `config/initializers/blorgh.rb` to learn what the class is.
|
||||
|
||||
There are now no strict dependencies on what the class is, only what the API for
|
||||
the class must be. The engine simply requires this class to define a
|
||||
`find_or_create_by` method which returns an object of that class, to be
|
||||
associated with a post when it's created. This object, of course, should have
|
||||
associated with an article when it's created. This object, of course, should have
|
||||
some sort of identifier by which it can be referenced.
|
||||
|
||||
#### General Engine Configuration
|
||||
|
@ -1107,12 +1107,12 @@ that isn't referenced by your main application.
|
|||
|
||||
#### Implementing Decorator Pattern Using Class#class_eval
|
||||
|
||||
**Adding** `Post#time_since_created`:
|
||||
**Adding** `Article#time_since_created`:
|
||||
|
||||
```ruby
|
||||
# MyApp/app/decorators/models/blorgh/post_decorator.rb
|
||||
# MyApp/app/decorators/models/blorgh/article_decorator.rb
|
||||
|
||||
Blorgh::Post.class_eval do
|
||||
Blorgh::Article.class_eval do
|
||||
def time_since_created
|
||||
Time.current - created_at
|
||||
end
|
||||
|
@ -1120,20 +1120,20 @@ end
|
|||
```
|
||||
|
||||
```ruby
|
||||
# Blorgh/app/models/post.rb
|
||||
# Blorgh/app/models/article.rb
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
has_many :comments
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
**Overriding** `Post#summary`:
|
||||
**Overriding** `Article#summary`:
|
||||
|
||||
```ruby
|
||||
# MyApp/app/decorators/models/blorgh/post_decorator.rb
|
||||
# MyApp/app/decorators/models/blorgh/article_decorator.rb
|
||||
|
||||
Blorgh::Post.class_eval do
|
||||
Blorgh::Article.class_eval do
|
||||
def summary
|
||||
"#{title} - #{truncate(text)}"
|
||||
end
|
||||
|
@ -1141,9 +1141,9 @@ end
|
|||
```
|
||||
|
||||
```ruby
|
||||
# Blorgh/app/models/post.rb
|
||||
# Blorgh/app/models/article.rb
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
has_many :comments
|
||||
def summary
|
||||
"#{title}"
|
||||
|
@ -1159,13 +1159,13 @@ class modifications, you might want to consider using [`ActiveSupport::Concern`]
|
|||
ActiveSupport::Concern manages load order of interlinked dependent modules and
|
||||
classes at run time allowing you to significantly modularize your code.
|
||||
|
||||
**Adding** `Post#time_since_created` and **Overriding** `Post#summary`:
|
||||
**Adding** `Article#time_since_created` and **Overriding** `Article#summary`:
|
||||
|
||||
```ruby
|
||||
# MyApp/app/models/blorgh/post.rb
|
||||
# MyApp/app/models/blorgh/article.rb
|
||||
|
||||
class Blorgh::Post < ActiveRecord::Base
|
||||
include Blorgh::Concerns::Models::Post
|
||||
class Blorgh::Article < ActiveRecord::Base
|
||||
include Blorgh::Concerns::Models::Article
|
||||
|
||||
def time_since_created
|
||||
Time.current - created_at
|
||||
|
@ -1178,22 +1178,22 @@ end
|
|||
```
|
||||
|
||||
```ruby
|
||||
# Blorgh/app/models/post.rb
|
||||
# Blorgh/app/models/article.rb
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
include Blorgh::Concerns::Models::Post
|
||||
class Article < ActiveRecord::Base
|
||||
include Blorgh::Concerns::Models::Article
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# Blorgh/lib/concerns/models/post
|
||||
# Blorgh/lib/concerns/models/article
|
||||
|
||||
module Blorgh::Concerns::Models::Post
|
||||
module Blorgh::Concerns::Models::Article
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
# 'included do' causes the included code to be evaluated in the
|
||||
# context where it is included (post.rb), rather than being
|
||||
# executed in the module's context (blorgh/concerns/models/post).
|
||||
# context where it is included (article.rb), rather than being
|
||||
# executed in the module's context (blorgh/concerns/models/article).
|
||||
included do
|
||||
attr_accessor :author_name
|
||||
belongs_to :author, class_name: "User"
|
||||
|
@ -1224,25 +1224,25 @@ When Rails looks for a view to render, it will first look in the `app/views`
|
|||
directory of the application. If it cannot find the view there, it will check in
|
||||
the `app/views` directories of all engines that have this directory.
|
||||
|
||||
When the application is asked to render the view for `Blorgh::PostsController`'s
|
||||
When the application is asked to render the view for `Blorgh::ArticlesController`'s
|
||||
index action, it will first look for the path
|
||||
`app/views/blorgh/posts/index.html.erb` within the application. If it cannot
|
||||
`app/views/blorgh/articles/index.html.erb` within the application. If it cannot
|
||||
find it, it will look inside the engine.
|
||||
|
||||
You can override this view in the application by simply creating a new file at
|
||||
`app/views/blorgh/posts/index.html.erb`. Then you can completely change what
|
||||
`app/views/blorgh/articles/index.html.erb`. Then you can completely change what
|
||||
this view would normally output.
|
||||
|
||||
Try this now by creating a new file at `app/views/blorgh/posts/index.html.erb`
|
||||
Try this now by creating a new file at `app/views/blorgh/articles/index.html.erb`
|
||||
and put this content in it:
|
||||
|
||||
```html+erb
|
||||
<h1>Posts</h1>
|
||||
<%= link_to "New Post", new_post_path %>
|
||||
<% @posts.each do |post| %>
|
||||
<h2><%= post.title %></h2>
|
||||
<small>By <%= post.author %></small>
|
||||
<%= simple_format(post.text) %>
|
||||
<h1>Articles</h1>
|
||||
<%= link_to "New Article", new_article_path %>
|
||||
<% @articles.each do |article| %>
|
||||
<h2><%= article.title %></h2>
|
||||
<small>By <%= article.author %></small>
|
||||
<%= simple_format(article.text) %>
|
||||
<hr>
|
||||
<% end %>
|
||||
```
|
||||
|
@ -1259,30 +1259,30 @@ Routes inside an engine are drawn on the `Engine` class within
|
|||
|
||||
```ruby
|
||||
Blorgh::Engine.routes.draw do
|
||||
resources :posts
|
||||
resources :articles
|
||||
end
|
||||
```
|
||||
|
||||
By having isolated routes such as this, if you wish to link to an area of an
|
||||
engine from within an application, you will need to use the engine's routing
|
||||
proxy method. Calls to normal routing methods such as `posts_path` may end up
|
||||
proxy method. Calls to normal routing methods such as `articles_path` may end up
|
||||
going to undesired locations if both the application and the engine have such a
|
||||
helper defined.
|
||||
|
||||
For instance, the following example would go to the application's `posts_path`
|
||||
if that template was rendered from the application, or the engine's `posts_path`
|
||||
For instance, the following example would go to the application's `articles_path`
|
||||
if that template was rendered from the application, or the engine's `articles_path`
|
||||
if it was rendered from the engine:
|
||||
|
||||
```erb
|
||||
<%= link_to "Blog posts", posts_path %>
|
||||
<%= link_to "Blog articles", articles_path %>
|
||||
```
|
||||
|
||||
To make this route always use the engine's `posts_path` routing helper method,
|
||||
To make this route always use the engine's `articles_path` routing helper method,
|
||||
we must call the method on the routing proxy method that shares the same name as
|
||||
the engine.
|
||||
|
||||
```erb
|
||||
<%= link_to "Blog posts", blorgh.posts_path %>
|
||||
<%= link_to "Blog articles", blorgh.articles_path %>
|
||||
```
|
||||
|
||||
If you wish to reference the application inside the engine in a similar way, use
|
||||
|
|
|
@ -279,10 +279,10 @@ end
|
|||
and see it in action when invoking the generator:
|
||||
|
||||
```bash
|
||||
$ bin/rails generate scaffold Post body:text
|
||||
$ bin/rails generate scaffold Article body:text
|
||||
[...]
|
||||
invoke my_helper
|
||||
create app/helpers/posts_helper.rb
|
||||
create app/helpers/articles_helper.rb
|
||||
```
|
||||
|
||||
We can notice on the output that our new helper was invoked instead of the Rails default. However one thing is missing, which is tests for our new generator and to do that, we are going to reuse old helpers test generators.
|
||||
|
|
|
@ -506,33 +506,33 @@ Layout declarations cascade downward in the hierarchy, and more specific layout
|
|||
end
|
||||
```
|
||||
|
||||
* `posts_controller.rb`
|
||||
* `articles_controller.rb`
|
||||
|
||||
```ruby
|
||||
class PostsController < ApplicationController
|
||||
class ArticlesController < ApplicationController
|
||||
end
|
||||
```
|
||||
|
||||
* `special_posts_controller.rb`
|
||||
* `special_articles_controller.rb`
|
||||
|
||||
```ruby
|
||||
class SpecialPostsController < PostsController
|
||||
class SpecialArticlesController < ArticlesController
|
||||
layout "special"
|
||||
end
|
||||
```
|
||||
|
||||
* `old_posts_controller.rb`
|
||||
* `old_articles_controller.rb`
|
||||
|
||||
```ruby
|
||||
class OldPostsController < SpecialPostsController
|
||||
class OldArticlesController < SpecialArticlesController
|
||||
layout false
|
||||
|
||||
def show
|
||||
@post = Post.find(params[:id])
|
||||
@article = Article.find(params[:id])
|
||||
end
|
||||
|
||||
def index
|
||||
@old_posts = Post.older
|
||||
@old_articles = Article.older
|
||||
render layout: "old"
|
||||
end
|
||||
# ...
|
||||
|
@ -542,10 +542,10 @@ Layout declarations cascade downward in the hierarchy, and more specific layout
|
|||
In this application:
|
||||
|
||||
* In general, views will be rendered in the `main` layout
|
||||
* `PostsController#index` will use the `main` layout
|
||||
* `SpecialPostsController#index` will use the `special` layout
|
||||
* `OldPostsController#show` will use no layout at all
|
||||
* `OldPostsController#index` will use the `old` layout
|
||||
* `ArticlesController#index` will use the `main` layout
|
||||
* `SpecialArticlesController#index` will use the `special` layout
|
||||
* `OldArticlesController#show` will use no layout at all
|
||||
* `OldArticlesController#index` will use the `old` layout
|
||||
|
||||
#### Avoiding Double Render Errors
|
||||
|
||||
|
|
|
@ -183,61 +183,61 @@ You may wish to organize groups of controllers under a namespace. Most commonly,
|
|||
|
||||
```ruby
|
||||
namespace :admin do
|
||||
resources :posts, :comments
|
||||
resources :articles, :comments
|
||||
end
|
||||
```
|
||||
|
||||
This will create a number of routes for each of the `posts` and `comments` controller. For `Admin::PostsController`, Rails will create:
|
||||
This will create a number of routes for each of the `articles` and `comments` controller. For `Admin::ArticlesController`, Rails will create:
|
||||
|
||||
| HTTP Verb | Path | Controller#Action | Named Helper |
|
||||
| --------- | --------------------- | ------------------- | ------------------------- |
|
||||
| GET | /admin/posts | admin/posts#index | admin_posts_path |
|
||||
| GET | /admin/posts/new | admin/posts#new | new_admin_post_path |
|
||||
| POST | /admin/posts | admin/posts#create | admin_posts_path |
|
||||
| GET | /admin/posts/:id | admin/posts#show | admin_post_path(:id) |
|
||||
| GET | /admin/posts/:id/edit | admin/posts#edit | edit_admin_post_path(:id) |
|
||||
| PATCH/PUT | /admin/posts/:id | admin/posts#update | admin_post_path(:id) |
|
||||
| DELETE | /admin/posts/:id | admin/posts#destroy | admin_post_path(:id) |
|
||||
| HTTP Verb | Path | Controller#Action | Named Helper |
|
||||
| --------- | ------------------------ | ---------------------- | ---------------------------- |
|
||||
| GET | /admin/articles | admin/articles#index | admin_articles_path |
|
||||
| GET | /admin/articles/new | admin/articles#new | new_admin_article_path |
|
||||
| POST | /admin/articles | admin/articles#create | admin_articles_path |
|
||||
| GET | /admin/articles/:id | admin/articles#show | admin_article_path(:id) |
|
||||
| GET | /admin/articles/:id/edit | admin/articles#edit | edit_admin_article_path(:id) |
|
||||
| PATCH/PUT | /admin/articles/:id | admin/articles#update | admin_article_path(:id) |
|
||||
| DELETE | /admin/articles/:id | admin/articles#destroy | admin_article_path(:id) |
|
||||
|
||||
If you want to route `/posts` (without the prefix `/admin`) to `Admin::PostsController`, you could use:
|
||||
If you want to route `/articles` (without the prefix `/admin`) to `Admin::ArticlesController`, you could use:
|
||||
|
||||
```ruby
|
||||
scope module: 'admin' do
|
||||
resources :posts, :comments
|
||||
resources :articles, :comments
|
||||
end
|
||||
```
|
||||
|
||||
or, for a single case:
|
||||
|
||||
```ruby
|
||||
resources :posts, module: 'admin'
|
||||
resources :articles, module: 'admin'
|
||||
```
|
||||
|
||||
If you want to route `/admin/posts` to `PostsController` (without the `Admin::` module prefix), you could use:
|
||||
If you want to route `/admin/articles` to `ArticlesController` (without the `Admin::` module prefix), you could use:
|
||||
|
||||
```ruby
|
||||
scope '/admin' do
|
||||
resources :posts, :comments
|
||||
resources :articles, :comments
|
||||
end
|
||||
```
|
||||
|
||||
or, for a single case:
|
||||
|
||||
```ruby
|
||||
resources :posts, path: '/admin/posts'
|
||||
resources :articles, path: '/admin/articles'
|
||||
```
|
||||
|
||||
In each of these cases, the named routes remain the same as if you did not use `scope`. In the last case, the following paths map to `PostsController`:
|
||||
|
||||
| HTTP Verb | Path | Controller#Action | Named Helper |
|
||||
| --------- | --------------------- | ----------------- | ------------------- |
|
||||
| GET | /admin/posts | posts#index | posts_path |
|
||||
| GET | /admin/posts/new | posts#new | new_post_path |
|
||||
| POST | /admin/posts | posts#create | posts_path |
|
||||
| GET | /admin/posts/:id | posts#show | post_path(:id) |
|
||||
| GET | /admin/posts/:id/edit | posts#edit | edit_post_path(:id) |
|
||||
| PATCH/PUT | /admin/posts/:id | posts#update | post_path(:id) |
|
||||
| DELETE | /admin/posts/:id | posts#destroy | post_path(:id) |
|
||||
| HTTP Verb | Path | Controller#Action | Named Helper |
|
||||
| --------- | ------------------------ | -------------------- | ---------------------- |
|
||||
| GET | /admin/articles | articles#index | articles_path |
|
||||
| GET | /admin/articles/new | articles#new | new_article_path |
|
||||
| POST | /admin/articles | articles#create | articles_path |
|
||||
| GET | /admin/articles/:id | articles#show | article_path(:id) |
|
||||
| GET | /admin/articles/:id/edit | articles#edit | edit_article_path(:id) |
|
||||
| PATCH/PUT | /admin/articles/:id | articles#update | article_path(:id) |
|
||||
| DELETE | /admin/articles/:id | articles#destroy | article_path(:id) |
|
||||
|
||||
TIP: _If you need to use a different controller namespace inside a `namespace` block you can specify an absolute controller path, e.g: `get '/foo' => '/foo#index'`._
|
||||
|
||||
|
@ -304,7 +304,7 @@ TIP: _Resources should never be nested more than 1 level deep._
|
|||
One way to avoid deep nesting (as recommended above) is to generate the collection actions scoped under the parent, so as to get a sense of the hierarchy, but to not nest the member actions. In other words, to only build routes with the minimal amount of information to uniquely identify the resource, like this:
|
||||
|
||||
```ruby
|
||||
resources :posts do
|
||||
resources :articles do
|
||||
resources :comments, only: [:index, :new, :create]
|
||||
end
|
||||
resources :comments, only: [:show, :edit, :update, :destroy]
|
||||
|
@ -313,7 +313,7 @@ resources :comments, only: [:show, :edit, :update, :destroy]
|
|||
This idea strikes a balance between descriptive routes and deep nesting. There exists shorthand syntax to achieve just that, via the `:shallow` option:
|
||||
|
||||
```ruby
|
||||
resources :posts do
|
||||
resources :articles do
|
||||
resources :comments, shallow: true
|
||||
end
|
||||
```
|
||||
|
@ -321,7 +321,7 @@ end
|
|||
This will generate the exact same routes as the first example. You can also specify the `:shallow` option in the parent resource, in which case all of the nested resources will be shallow:
|
||||
|
||||
```ruby
|
||||
resources :posts, shallow: true do
|
||||
resources :articles, shallow: true do
|
||||
resources :comments
|
||||
resources :quotes
|
||||
resources :drafts
|
||||
|
@ -332,7 +332,7 @@ The `shallow` method of the DSL creates a scope inside of which every nesting is
|
|||
|
||||
```ruby
|
||||
shallow do
|
||||
resources :posts do
|
||||
resources :articles do
|
||||
resources :comments
|
||||
resources :quotes
|
||||
resources :drafts
|
||||
|
@ -344,7 +344,7 @@ There exist two options for `scope` to customize shallow routes. `:shallow_path`
|
|||
|
||||
```ruby
|
||||
scope shallow_path: "sekret" do
|
||||
resources :posts do
|
||||
resources :articles do
|
||||
resources :comments, shallow: true
|
||||
end
|
||||
end
|
||||
|
@ -352,21 +352,21 @@ end
|
|||
|
||||
The comments resource here will have the following routes generated for it:
|
||||
|
||||
| HTTP Verb | Path | Controller#Action | Named Helper |
|
||||
| --------- | -------------------------------------- | ----------------- | --------------------- |
|
||||
| GET | /posts/:post_id/comments(.:format) | comments#index | post_comments_path |
|
||||
| POST | /posts/:post_id/comments(.:format) | comments#create | post_comments_path |
|
||||
| GET | /posts/:post_id/comments/new(.:format) | comments#new | new_post_comment_path |
|
||||
| GET | /sekret/comments/:id/edit(.:format) | comments#edit | edit_comment_path |
|
||||
| GET | /sekret/comments/:id(.:format) | comments#show | comment_path |
|
||||
| PATCH/PUT | /sekret/comments/:id(.:format) | comments#update | comment_path |
|
||||
| DELETE | /sekret/comments/:id(.:format) | comments#destroy | comment_path |
|
||||
| HTTP Verb | Path | Controller#Action | Named Helper |
|
||||
| --------- | -------------------------------------------- | ----------------- | ------------------------ |
|
||||
| GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
|
||||
| POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
|
||||
| GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
|
||||
| GET | /sekret/comments/:id/edit(.:format) | comments#edit | edit_comment_path |
|
||||
| GET | /sekret/comments/:id(.:format) | comments#show | comment_path |
|
||||
| PATCH/PUT | /sekret/comments/:id(.:format) | comments#update | comment_path |
|
||||
| DELETE | /sekret/comments/:id(.:format) | comments#destroy | comment_path |
|
||||
|
||||
The `:shallow_prefix` option adds the specified parameter to the named helpers:
|
||||
|
||||
```ruby
|
||||
scope shallow_prefix: "sekret" do
|
||||
resources :posts do
|
||||
resources :articles do
|
||||
resources :comments, shallow: true
|
||||
end
|
||||
end
|
||||
|
@ -374,15 +374,15 @@ end
|
|||
|
||||
The comments resource here will have the following routes generated for it:
|
||||
|
||||
| HTTP Verb | Path | Controller#Action | Named Helper |
|
||||
| --------- | -------------------------------------- | ----------------- | ------------------------ |
|
||||
| GET | /posts/:post_id/comments(.:format) | comments#index | post_comments_path |
|
||||
| POST | /posts/:post_id/comments(.:format) | comments#create | post_comments_path |
|
||||
| GET | /posts/:post_id/comments/new(.:format) | comments#new | new_post_comment_path |
|
||||
| GET | /comments/:id/edit(.:format) | comments#edit | edit_sekret_comment_path |
|
||||
| GET | /comments/:id(.:format) | comments#show | sekret_comment_path |
|
||||
| PATCH/PUT | /comments/:id(.:format) | comments#update | sekret_comment_path |
|
||||
| DELETE | /comments/:id(.:format) | comments#destroy | sekret_comment_path |
|
||||
| HTTP Verb | Path | Controller#Action | Named Helper |
|
||||
| --------- | -------------------------------------------- | ----------------- | --------------------------- |
|
||||
| GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
|
||||
| POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
|
||||
| GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
|
||||
| GET | /comments/:id/edit(.:format) | comments#edit | edit_sekret_comment_path |
|
||||
| GET | /comments/:id(.:format) | comments#show | sekret_comment_path |
|
||||
| PATCH/PUT | /comments/:id(.:format) | comments#update | sekret_comment_path |
|
||||
| DELETE | /comments/:id(.:format) | comments#destroy | sekret_comment_path |
|
||||
|
||||
### Routing concerns
|
||||
|
||||
|
@ -403,7 +403,7 @@ These concerns can be used in resources to avoid code duplication and share beha
|
|||
```ruby
|
||||
resources :messages, concerns: :commentable
|
||||
|
||||
resources :posts, concerns: [:commentable, :image_attachable]
|
||||
resources :articles, concerns: [:commentable, :image_attachable]
|
||||
```
|
||||
|
||||
The above is equivalent to:
|
||||
|
@ -413,7 +413,7 @@ resources :messages do
|
|||
resources :comments
|
||||
end
|
||||
|
||||
resources :posts do
|
||||
resources :articles do
|
||||
resources :comments
|
||||
resources :images, only: :index
|
||||
end
|
||||
|
@ -422,7 +422,7 @@ end
|
|||
Also you can use them in any place that you want inside the routes, for example in a scope or namespace call:
|
||||
|
||||
```ruby
|
||||
namespace :posts do
|
||||
namespace :articles do
|
||||
concerns :commentable
|
||||
end
|
||||
```
|
||||
|
@ -662,15 +662,15 @@ get 'photos/:id', to: 'photos#show', id: /[A-Z]\d{5}/
|
|||
`:constraints` takes regular expressions with the restriction that regexp anchors can't be used. For example, the following route will not work:
|
||||
|
||||
```ruby
|
||||
get '/:id', to: 'posts#show', constraints: { id: /^\d/ }
|
||||
get '/:id', to: 'articles#show', constraints: { id: /^\d/ }
|
||||
```
|
||||
|
||||
However, note that you don't need to use anchors because all routes are anchored at the start.
|
||||
|
||||
For example, the following routes would allow for `posts` with `to_param` values like `1-hello-world` that always begin with a number and `users` with `to_param` values like `david` that never begin with a number to share the root namespace:
|
||||
For example, the following routes would allow for `articles` with `to_param` values like `1-hello-world` that always begin with a number and `users` with `to_param` values like `david` that never begin with a number to share the root namespace:
|
||||
|
||||
```ruby
|
||||
get '/:id', to: 'posts#show', constraints: { id: /\d.+/ }
|
||||
get '/:id', to: 'articles#show', constraints: { id: /\d.+/ }
|
||||
get '/:username', to: 'users#show'
|
||||
```
|
||||
|
||||
|
@ -771,20 +771,20 @@ get '*pages', to: 'pages#show', format: true
|
|||
You can redirect any path to another path using the `redirect` helper in your router:
|
||||
|
||||
```ruby
|
||||
get '/stories', to: redirect('/posts')
|
||||
get '/stories', to: redirect('/articles')
|
||||
```
|
||||
|
||||
You can also reuse dynamic segments from the match in the path to redirect to:
|
||||
|
||||
```ruby
|
||||
get '/stories/:name', to: redirect('/posts/%{name}')
|
||||
get '/stories/:name', to: redirect('/articles/%{name}')
|
||||
```
|
||||
|
||||
You can also provide a block to redirect, which receives the symbolized path parameters and the request object:
|
||||
|
||||
```ruby
|
||||
get '/stories/:name', to: redirect { |path_params, req| "/posts/#{path_params[:name].pluralize}" }
|
||||
get '/stories', to: redirect { |path_params, req| "/posts/#{req.subdomain}" }
|
||||
get '/stories/:name', to: redirect { |path_params, req| "/articles/#{path_params[:name].pluralize}" }
|
||||
get '/stories', to: redirect { |path_params, req| "/articles/#{req.subdomain}" }
|
||||
```
|
||||
|
||||
Please note that this redirection is a 301 "Moved Permanently" redirect. Keep in mind that some web browsers or proxy servers will cache this type of redirect, making the old page inaccessible.
|
||||
|
@ -793,7 +793,7 @@ In all of these cases, if you don't provide the leading host (`http://www.exampl
|
|||
|
||||
### Routing to Rack Applications
|
||||
|
||||
Instead of a String like `'posts#index'`, which corresponds to the `index` action in the `PostsController`, you can specify any [Rack application](rails_on_rack.html) as the endpoint for a matcher:
|
||||
Instead of a String like `'articles#index'`, which corresponds to the `index` action in the `ArticlesController`, you can specify any [Rack application](rails_on_rack.html) as the endpoint for a matcher:
|
||||
|
||||
```ruby
|
||||
match '/application.js', to: Sprockets, via: :all
|
||||
|
@ -801,7 +801,7 @@ match '/application.js', to: Sprockets, via: :all
|
|||
|
||||
As long as `Sprockets` responds to `call` and returns a `[status, headers, body]`, the router won't know the difference between the Rack application and an action. This is an appropriate use of `via: :all`, as you will want to allow your Rack application to handle all verbs as it considers appropriate.
|
||||
|
||||
NOTE: For the curious, `'posts#index'` actually expands out to `PostsController.action(:index)`, which returns a valid Rack application.
|
||||
NOTE: For the curious, `'articles#index'` actually expands out to `ArticlesController.action(:index)`, which returns a valid Rack application.
|
||||
|
||||
### Using `root`
|
||||
|
||||
|
@ -837,7 +837,7 @@ get 'こんにちは', to: 'welcome#index'
|
|||
Customizing Resourceful Routes
|
||||
------------------------------
|
||||
|
||||
While the default routes and helpers generated by `resources :posts` will usually serve you well, you may want to customize them in some way. Rails allows you to customize virtually any generic part of the resourceful helpers.
|
||||
While the default routes and helpers generated by `resources :articles` will usually serve you well, you may want to customize them in some way. Rails allows you to customize virtually any generic part of the resourceful helpers.
|
||||
|
||||
### Specifying a Controller to Use
|
||||
|
||||
|
@ -974,11 +974,11 @@ You can prefix routes with a named parameter also:
|
|||
|
||||
```ruby
|
||||
scope ':username' do
|
||||
resources :posts
|
||||
resources :articles
|
||||
end
|
||||
```
|
||||
|
||||
This will provide you with URLs such as `/bob/posts/1` and will allow you to reference the `username` part of the path as `params[:username]` in controllers, helpers and views.
|
||||
This will provide you with URLs such as `/bob/articles/1` and will allow you to reference the `username` part of the path as `params[:username]` in controllers, helpers and views.
|
||||
|
||||
### Restricting the Routes Created
|
||||
|
||||
|
|
|
@ -141,20 +141,20 @@ NOTE: For more information on Rails <i>scaffolding</i>, refer to [Getting Starte
|
|||
When you use `rails generate scaffold`, for a resource among other things it creates a test stub in the `test/models` folder:
|
||||
|
||||
```bash
|
||||
$ bin/rails generate scaffold post title:string body:text
|
||||
$ bin/rails generate scaffold article title:string body:text
|
||||
...
|
||||
create app/models/post.rb
|
||||
create test/models/post_test.rb
|
||||
create test/fixtures/posts.yml
|
||||
create app/models/article.rb
|
||||
create test/models/article_test.rb
|
||||
create test/fixtures/articles.yml
|
||||
...
|
||||
```
|
||||
|
||||
The default test stub in `test/models/post_test.rb` looks like this:
|
||||
The default test stub in `test/models/article_test.rb` looks like this:
|
||||
|
||||
```ruby
|
||||
require 'test_helper'
|
||||
|
||||
class PostTest < ActiveSupport::TestCase
|
||||
class ArticleTest < ActiveSupport::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
|
@ -170,10 +170,10 @@ require 'test_helper'
|
|||
As you know by now, `test_helper.rb` specifies the default configuration to run our tests. This is included with all the tests, so any methods added to this file are available to all your tests.
|
||||
|
||||
```ruby
|
||||
class PostTest < ActiveSupport::TestCase
|
||||
class ArticleTest < ActiveSupport::TestCase
|
||||
```
|
||||
|
||||
The `PostTest` class defines a _test case_ because it inherits from `ActiveSupport::TestCase`. `PostTest` thus has all the methods available from `ActiveSupport::TestCase`. You'll see those methods a little later in this guide.
|
||||
The `ArticleTest` class defines a _test case_ because it inherits from `ActiveSupport::TestCase`. `ArticleTest` thus has all the methods available from `ActiveSupport::TestCase`. You'll see those methods a little later in this guide.
|
||||
|
||||
Any method defined within a class inherited from `MiniTest::Unit::TestCase`
|
||||
(which is the superclass of `ActiveSupport::TestCase`) that begins with `test` (case sensitive) is simply called a test. So, `test_password`, `test_valid_password` and `testValidPassword` all are legal test names and are run automatically when the test case is run.
|
||||
|
@ -220,7 +220,7 @@ In order to run your tests, your test database will need to have the current str
|
|||
Running a test is as simple as invoking the file containing the test cases through `rake test` command.
|
||||
|
||||
```bash
|
||||
$ bin/rake test test/models/post_test.rb
|
||||
$ bin/rake test test/models/article_test.rb
|
||||
.
|
||||
|
||||
Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s.
|
||||
|
@ -231,7 +231,7 @@ Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s.
|
|||
You can also run a particular test method from the test case by running the test and providing the `test method name`.
|
||||
|
||||
```bash
|
||||
$ bin/rake test test/models/post_test.rb test_the_truth
|
||||
$ bin/rake test test/models/article_test.rb test_the_truth
|
||||
.
|
||||
|
||||
Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s.
|
||||
|
@ -243,25 +243,25 @@ This will run all test methods from the test case. Note that `test_helper.rb` is
|
|||
|
||||
The `.` (dot) above indicates a passing test. When a test fails you see an `F`; when a test throws an error you see an `E` in its place. The last line of the output is the summary.
|
||||
|
||||
To see how a test failure is reported, you can add a failing test to the `post_test.rb` test case.
|
||||
To see how a test failure is reported, you can add a failing test to the `article_test.rb` test case.
|
||||
|
||||
```ruby
|
||||
test "should not save post without title" do
|
||||
post = Post.new
|
||||
assert_not post.save
|
||||
test "should not save article without title" do
|
||||
article = Article.new
|
||||
assert_not article.save
|
||||
end
|
||||
```
|
||||
|
||||
Let us run this newly added test.
|
||||
|
||||
```bash
|
||||
$ bin/rake test test/models/post_test.rb test_should_not_save_post_without_title
|
||||
$ bin/rake test test/models/article_test.rb test_should_not_save_article_without_title
|
||||
F
|
||||
|
||||
Finished tests in 0.044632s, 22.4054 tests/s, 22.4054 assertions/s.
|
||||
|
||||
1) Failure:
|
||||
test_should_not_save_post_without_title(PostTest) [test/models/post_test.rb:6]:
|
||||
test_should_not_save_article_without_title(ArticleTest) [test/models/article_test.rb:6]:
|
||||
Failed assertion, no message given.
|
||||
|
||||
1 tests, 1 assertions, 1 failures, 0 errors, 0 skips
|
||||
|
@ -270,9 +270,9 @@ Failed assertion, no message given.
|
|||
In the output, `F` denotes a failure. You can see the corresponding trace shown under `1)` along with the name of the failing test. The next few lines contain the stack trace followed by a message which mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable, every assertion provides an optional message parameter, as shown here:
|
||||
|
||||
```ruby
|
||||
test "should not save post without title" do
|
||||
post = Post.new
|
||||
assert_not post.save, "Saved the post without a title"
|
||||
test "should not save article without title" do
|
||||
article = Article.new
|
||||
assert_not article.save, "Saved the article without a title"
|
||||
end
|
||||
```
|
||||
|
||||
|
@ -280,14 +280,14 @@ Running this test shows the friendlier assertion message:
|
|||
|
||||
```bash
|
||||
1) Failure:
|
||||
test_should_not_save_post_without_title(PostTest) [test/models/post_test.rb:6]:
|
||||
Saved the post without a title
|
||||
test_should_not_save_article_without_title(ArticleTest) [test/models/article_test.rb:6]:
|
||||
Saved the article without a title
|
||||
```
|
||||
|
||||
Now to get this test to pass we can add a model level validation for the _title_ field.
|
||||
|
||||
```ruby
|
||||
class Post < ActiveRecord::Base
|
||||
class Article < ActiveRecord::Base
|
||||
validates :title, presence: true
|
||||
end
|
||||
```
|
||||
|
@ -295,7 +295,7 @@ end
|
|||
Now the test should pass. Let us verify by running the test again:
|
||||
|
||||
```bash
|
||||
$ bin/rake test test/models/post_test.rb test_should_not_save_post_without_title
|
||||
$ bin/rake test test/models/article_test.rb test_should_not_save_article_without_title
|
||||
.
|
||||
|
||||
Finished tests in 0.047721s, 20.9551 tests/s, 20.9551 assertions/s.
|
||||
|
@ -320,15 +320,15 @@ end
|
|||
Now you can see even more output in the console from running the tests:
|
||||
|
||||
```bash
|
||||
$ bin/rake test test/models/post_test.rb test_should_report_error
|
||||
$ bin/rake test test/models/article_test.rb test_should_report_error
|
||||
E
|
||||
|
||||
Finished tests in 0.030974s, 32.2851 tests/s, 0.0000 assertions/s.
|
||||
|
||||
1) Error:
|
||||
test_should_report_error(PostTest):
|
||||
NameError: undefined local variable or method `some_undefined_variable' for #<PostTest:0x007fe32e24afe0>
|
||||
test/models/post_test.rb:10:in `block in <class:PostTest>'
|
||||
test_should_report_error(ArticleTest):
|
||||
NameError: undefined local variable or method `some_undefined_variable' for #<ArticleTest:0x007fe32e24afe0>
|
||||
test/models/article_test.rb:10:in `block in <class:ArticleTest>'
|
||||
|
||||
1 tests, 0 assertions, 0 failures, 1 errors, 0 skips
|
||||
```
|
||||
|
@ -345,7 +345,7 @@ backtrace. simply set the `BACKTRACE` environment variable to enable this
|
|||
behavior:
|
||||
|
||||
```bash
|
||||
$ BACKTRACE=1 bin/rake test test/models/post_test.rb
|
||||
$ BACKTRACE=1 bin/rake test test/models/article_test.rb
|
||||
```
|
||||
|
||||
### What to Include in Your Unit Tests
|
||||
|
@ -422,26 +422,26 @@ You should test for things such as:
|
|||
* was the correct object stored in the response template?
|
||||
* was the appropriate message displayed to the user in the view?
|
||||
|
||||
Now that we have used Rails scaffold generator for our `Post` resource, it has already created the controller code and tests. You can take look at the file `posts_controller_test.rb` in the `test/controllers` directory.
|
||||
Now that we have used Rails scaffold generator for our `Article` resource, it has already created the controller code and tests. You can take look at the file `articles_controller_test.rb` in the `test/controllers` directory.
|
||||
|
||||
Let me take you through one such test, `test_should_get_index` from the file `posts_controller_test.rb`.
|
||||
Let me take you through one such test, `test_should_get_index` from the file `articles_controller_test.rb`.
|
||||
|
||||
```ruby
|
||||
class PostsControllerTest < ActionController::TestCase
|
||||
class ArticlesControllerTest < ActionController::TestCase
|
||||
test "should get index" do
|
||||
get :index
|
||||
assert_response :success
|
||||
assert_not_nil assigns(:posts)
|
||||
assert_not_nil assigns(:articles)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
In the `test_should_get_index` test, Rails simulates a request on the action called `index`, making sure the request was successful and also ensuring that it assigns a valid `posts` instance variable.
|
||||
In the `test_should_get_index` test, Rails simulates a request on the action called `index`, making sure the request was successful and also ensuring that it assigns a valid `articles` instance variable.
|
||||
|
||||
The `get` method kicks off the web request and populates the results into the response. It accepts 4 arguments:
|
||||
|
||||
* The action of the controller you are requesting. This can be in the form of a string or a symbol.
|
||||
* An optional hash of request parameters to pass into the action (eg. query string parameters or post variables).
|
||||
* An optional hash of request parameters to pass into the action (eg. query string parameters or article variables).
|
||||
* An optional hash of session variables to pass along with the request.
|
||||
* An optional hash of flash values.
|
||||
|
||||
|
@ -457,17 +457,17 @@ Another example: Calling the `:view` action, passing an `id` of 12 as the `param
|
|||
get(:view, {'id' => '12'}, nil, {'message' => 'booya!'})
|
||||
```
|
||||
|
||||
NOTE: If you try running `test_should_create_post` test from `posts_controller_test.rb` it will fail on account of the newly added model level validation and rightly so.
|
||||
NOTE: If you try running `test_should_create_article` test from `articles_controller_test.rb` it will fail on account of the newly added model level validation and rightly so.
|
||||
|
||||
Let us modify `test_should_create_post` test in `posts_controller_test.rb` so that all our test pass:
|
||||
Let us modify `test_should_create_article` test in `articles_controller_test.rb` so that all our test pass:
|
||||
|
||||
```ruby
|
||||
test "should create post" do
|
||||
assert_difference('Post.count') do
|
||||
post :create, post: {title: 'Some title'}
|
||||
test "should create article" do
|
||||
assert_difference('Article.count') do
|
||||
post :create, article: {title: 'Some title'}
|
||||
end
|
||||
|
||||
assert_redirected_to post_path(assigns(:post))
|
||||
assert_redirected_to article_path(assigns(:article))
|
||||
end
|
||||
```
|
||||
|
||||
|
@ -576,12 +576,12 @@ is the correct way to assert for the layout when the view renders a partial with
|
|||
Here's another example that uses `flash`, `assert_redirected_to`, and `assert_difference`:
|
||||
|
||||
```ruby
|
||||
test "should create post" do
|
||||
assert_difference('Post.count') do
|
||||
post :create, post: {title: 'Hi', body: 'This is my first post.'}
|
||||
test "should create article" do
|
||||
assert_difference('article.count') do
|
||||
post :create, article: {title: 'Hi', body: 'This is my first article.'}
|
||||
end
|
||||
assert_redirected_to post_path(assigns(:post))
|
||||
assert_equal 'Post was successfully created.', flash[:notice]
|
||||
assert_redirected_to article_path(assigns(:article))
|
||||
assert_equal 'Article was successfully created.', flash[:notice]
|
||||
end
|
||||
```
|
||||
|
||||
|
@ -712,7 +712,7 @@ class UserFlowsTest < ActionDispatch::IntegrationTest
|
|||
assert_equal 'Welcome david!', flash[:notice]
|
||||
|
||||
https!(false)
|
||||
get "/posts/all"
|
||||
get "/articles/all"
|
||||
assert_response :success
|
||||
assert assigns(:products)
|
||||
end
|
||||
|
@ -807,43 +807,43 @@ For more information on `MiniTest`, refer to [Minitest](http://www.ruby-doc.org/
|
|||
Setup and Teardown
|
||||
------------------
|
||||
|
||||
If you would like to run a block of code before the start of each test and another block of code after the end of each test you have two special callbacks for your rescue. Let's take note of this by looking at an example for our functional test in `Posts` controller:
|
||||
If you would like to run a block of code before the start of each test and another block of code after the end of each test you have two special callbacks for your rescue. Let's take note of this by looking at an example for our functional test in `Articles` controller:
|
||||
|
||||
```ruby
|
||||
require 'test_helper'
|
||||
|
||||
class PostsControllerTest < ActionController::TestCase
|
||||
class ArticlesControllerTest < ActionController::TestCase
|
||||
|
||||
# called before every single test
|
||||
def setup
|
||||
@post = posts(:one)
|
||||
@article = articles(:one)
|
||||
end
|
||||
|
||||
# called after every single test
|
||||
def teardown
|
||||
# as we are re-initializing @post before every test
|
||||
# as we are re-initializing @article before every test
|
||||
# setting it to nil here is not essential but I hope
|
||||
# you understand how you can use the teardown method
|
||||
@post = nil
|
||||
@article = nil
|
||||
end
|
||||
|
||||
test "should show post" do
|
||||
get :show, id: @post.id
|
||||
test "should show article" do
|
||||
get :show, id: @article.id
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should destroy post" do
|
||||
assert_difference('Post.count', -1) do
|
||||
delete :destroy, id: @post.id
|
||||
test "should destroy article" do
|
||||
assert_difference('Article.count', -1) do
|
||||
delete :destroy, id: @article.id
|
||||
end
|
||||
|
||||
assert_redirected_to posts_path
|
||||
assert_redirected_to articles_path
|
||||
end
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
Above, the `setup` method is called before each test and so `@post` is available for each of the tests. Rails implements `setup` and `teardown` as `ActiveSupport::Callbacks`. Which essentially means you need not only use `setup` and `teardown` as methods in your tests. You could specify them by using:
|
||||
Above, the `setup` method is called before each test and so `@article` is available for each of the tests. Rails implements `setup` and `teardown` as `ActiveSupport::Callbacks`. Which essentially means you need not only use `setup` and `teardown` as methods in your tests. You could specify them by using:
|
||||
|
||||
* a block
|
||||
* a method (like in the earlier example)
|
||||
|
@ -855,38 +855,38 @@ Let's see the earlier example by specifying `setup` callback by specifying a met
|
|||
```ruby
|
||||
require 'test_helper'
|
||||
|
||||
class PostsControllerTest < ActionController::TestCase
|
||||
class ArticlesControllerTest < ActionController::TestCase
|
||||
|
||||
# called before every single test
|
||||
setup :initialize_post
|
||||
setup :initialize_article
|
||||
|
||||
# called after every single test
|
||||
def teardown
|
||||
@post = nil
|
||||
@article = nil
|
||||
end
|
||||
|
||||
test "should show post" do
|
||||
get :show, id: @post.id
|
||||
test "should show article" do
|
||||
get :show, id: @article.id
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should update post" do
|
||||
patch :update, id: @post.id, post: {}
|
||||
assert_redirected_to post_path(assigns(:post))
|
||||
test "should update article" do
|
||||
patch :update, id: @article.id, article: {}
|
||||
assert_redirected_to article_path(assigns(:article))
|
||||
end
|
||||
|
||||
test "should destroy post" do
|
||||
assert_difference('Post.count', -1) do
|
||||
delete :destroy, id: @post.id
|
||||
test "should destroy article" do
|
||||
assert_difference('Article.count', -1) do
|
||||
delete :destroy, id: @article.id
|
||||
end
|
||||
|
||||
assert_redirected_to posts_path
|
||||
assert_redirected_to articles_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize_post
|
||||
@post = posts(:one)
|
||||
def initialize_article
|
||||
@article = articles(:one)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
@ -894,11 +894,11 @@ end
|
|||
Testing Routes
|
||||
--------------
|
||||
|
||||
Like everything else in your Rails application, it is recommended that you test your routes. An example test for a route in the default `show` action of `Posts` controller above should look like:
|
||||
Like everything else in your Rails application, it is recommended that you test your routes. An example test for a route in the default `show` action of `Articles` controller above should look like:
|
||||
|
||||
```ruby
|
||||
test "should route to post" do
|
||||
assert_routing '/posts/1', {controller: "posts", action: "show", id: "1"}
|
||||
test "should route to article" do
|
||||
assert_routing '/articles/1', {controller: "articles", action: "show", id: "1"}
|
||||
end
|
||||
```
|
||||
|
||||
|
|
|
@ -488,7 +488,7 @@ def update
|
|||
respond_to do |format|
|
||||
format.json do
|
||||
# perform a partial update
|
||||
@post.update params[:post]
|
||||
@article.update params[:article]
|
||||
end
|
||||
|
||||
format.json_patch do
|
||||
|
|
|
@ -158,7 +158,7 @@ is a helper that assists with writing forms. `form_for` takes a `:remote`
|
|||
option. It works like this:
|
||||
|
||||
```erb
|
||||
<%= form_for(@post, remote: true) do |f| %>
|
||||
<%= form_for(@article, remote: true) do |f| %>
|
||||
...
|
||||
<% end %>
|
||||
```
|
||||
|
@ -166,7 +166,7 @@ option. It works like this:
|
|||
This will generate the following HTML:
|
||||
|
||||
```html
|
||||
<form accept-charset="UTF-8" action="/posts" class="new_post" data-remote="true" id="new_post" method="post">
|
||||
<form accept-charset="UTF-8" action="/articles" class="new_article" data-remote="true" id="new_article" method="post">
|
||||
...
|
||||
</form>
|
||||
```
|
||||
|
@ -180,10 +180,10 @@ bind to the `ajax:success` event. On failure, use `ajax:error`. Check it out:
|
|||
|
||||
```coffeescript
|
||||
$(document).ready ->
|
||||
$("#new_post").on("ajax:success", (e, data, status, xhr) ->
|
||||
$("#new_post").append xhr.responseText
|
||||
$("#new_article").on("ajax:success", (e, data, status, xhr) ->
|
||||
$("#new_article").append xhr.responseText
|
||||
).on "ajax:error", (e, xhr, status, error) ->
|
||||
$("#new_post").append "<p>ERROR</p>"
|
||||
$("#new_article").append "<p>ERROR</p>"
|
||||
```
|
||||
|
||||
Obviously, you'll want to be a bit more sophisticated than that, but it's a
|
||||
|
@ -196,7 +196,7 @@ is very similar to `form_for`. It has a `:remote` option that you can use like
|
|||
this:
|
||||
|
||||
```erb
|
||||
<%= form_tag('/posts', remote: true) do %>
|
||||
<%= form_tag('/articles', remote: true) do %>
|
||||
...
|
||||
<% end %>
|
||||
```
|
||||
|
@ -204,7 +204,7 @@ this:
|
|||
This will generate the following HTML:
|
||||
|
||||
```html
|
||||
<form accept-charset="UTF-8" action="/posts" data-remote="true" method="post">
|
||||
<form accept-charset="UTF-8" action="/articles" data-remote="true" method="post">
|
||||
...
|
||||
</form>
|
||||
```
|
||||
|
@ -219,21 +219,21 @@ is a helper that assists with generating links. It has a `:remote` option you
|
|||
can use like this:
|
||||
|
||||
```erb
|
||||
<%= link_to "a post", @post, remote: true %>
|
||||
<%= link_to "an article", @article, remote: true %>
|
||||
```
|
||||
|
||||
which generates
|
||||
|
||||
```html
|
||||
<a href="/posts/1" data-remote="true">a post</a>
|
||||
<a href="/articles/1" data-remote="true">an article</a>
|
||||
```
|
||||
|
||||
You can bind to the same Ajax events as `form_for`. Here's an example. Let's
|
||||
assume that we have a list of posts that can be deleted with just one
|
||||
assume that we have a list of articles that can be deleted with just one
|
||||
click. We would generate some HTML like this:
|
||||
|
||||
```erb
|
||||
<%= link_to "Delete post", @post, remote: true, method: :delete %>
|
||||
<%= link_to "Delete article", @article, remote: true, method: :delete %>
|
||||
```
|
||||
|
||||
and write some CoffeeScript like this:
|
||||
|
@ -241,7 +241,7 @@ and write some CoffeeScript like this:
|
|||
```coffeescript
|
||||
$ ->
|
||||
$("a[data-remote]").on "ajax:success", (e, data, status, xhr) ->
|
||||
alert "The post was deleted."
|
||||
alert "The article was deleted."
|
||||
```
|
||||
|
||||
### button_to
|
||||
|
@ -249,14 +249,14 @@ $ ->
|
|||
[`button_to`](http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to) is a helper that helps you create buttons. It has a `:remote` option that you can call like this:
|
||||
|
||||
```erb
|
||||
<%= button_to "A post", @post, remote: true %>
|
||||
<%= button_to "An article", @article, remote: true %>
|
||||
```
|
||||
|
||||
this generates
|
||||
|
||||
```html
|
||||
<form action="/posts/1" class="button_to" data-remote="true" method="post">
|
||||
<div><input type="submit" value="A post"></div>
|
||||
<form action="/articles/1" class="button_to" data-remote="true" method="post">
|
||||
<div><input type="submit" value="An article"></div>
|
||||
</form>
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in a new issue