Rename Posts to Articles in Guides, continuation of 2d446e77 / #13774 [ci skip]

This commit is contained in:
John Kelly Ferguson 2014-05-21 21:47:18 -04:00
parent 2fb2913b35
commit d02c810e29
20 changed files with 657 additions and 654 deletions

View File

@ -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.

View File

@ -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).

View File

@ -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.

View File

@ -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
```

View File

@ -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)
```

View File

@ -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>
```

View File

@ -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!

View File

@ -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.

View File

@ -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?

View File

@ -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!
```

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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
```

View File

@ -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

View File

@ -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>
```