- 2.1 Planning the Application
- 2.2 The Users Resource
- 2.3 The Microposts Resource
- 2.4 Conclusion
- 2.5 Exercises
2.3 The Microposts Resource
Having generated and explored the Users resource, we turn now to the associated Microposts resource. Throughout this section, we recommend comparing the elements of the Microposts resource with the analogous user elements from Section 2.2; the two resources parallel each other in many ways. The RESTful structure of Rails applications is best absorbed by this sort of repetition of form—indeed, seeing the parallel structure of Users and Microposts even at this early stage is one of the prime motivations for this chapter.
2.3.1 A Micropost Microtour
As with the Users resource, we’ll generate scaffold code for the Microposts resource using rails generate scaffold, in this case implementing the data model from Figure 2.3:8
$ rails generate scaffold Micropost content:text user_id:integer
invoke active_record create db/migrate/20140821012832_create_microposts.rb create app/models/micropost.rb invoke test_unit create test/models/micropost_test.rb create test/fixtures/microposts.yml invoke resource_route route resources :microposts invoke scaffold_controller create app/controllers/microposts_controller.rb invoke erb create app/views/microposts create app/views/microposts/index.html.erb create app/views/microposts/edit.html.erb create app/views/microposts/show.html.erb create app/views/microposts/new.html.erb create app/views/microposts/_form.html.erb invoke test_unit create test/controllers/microposts_controller_test.rb invoke helper create app/helpers/microposts_helper.rb invoke test_unit create test/helpers/microposts_helper_test.rb invoke jbuilder create app/views/microposts/index.json.jbuilder create app/views/microposts/show.json.jbuilder invoke assets invoke coffee create app/assets/javascripts/microposts.js.coffee invoke scss create app/assets/stylesheets/microposts.css.scss invoke scss identical app/assets/stylesheets/scaffolds.css.scss
(If you get an error related to Spring, just run the command again.) To update our database with the new data model, we need to run a migration as in Section 2.2:
$ bundle exec rake db:migrate
== CreateMicroposts: migrating =========================================== -- create_table(:microposts) -> 0.0023s == CreateMicroposts: migrated (0.0026s) ====================================
Now we are in a position to create microposts in the same way we created users in Section 2.2.1. As you might guess, the scaffold generator has updated the Rails routes file with a rule for Microposts resource, as seen in Listing 2.8.9 As with users, the resources :microposts routing rule maps micropost URLs to actions in the Microposts controller, as seen in Table 2.3.
Listing 2.8 The Rails routes, with a new rule for Microposts resources.
config/routes.rb
____________________________________________________________________________
Rails.application.routes.draw do
resources :microposts
resources :users
.
.
.
end
____________________________________________________________________________
Table 2.3 RESTful routes provided by the Microposts resource in Listing 2.8
HTTP request |
URL |
Action |
Purpose |
GET |
/microposts |
index |
page to list all microposts |
GET |
/microposts/1 |
show |
page to show micropost with id 1 |
GET |
/microposts/new |
new |
page to make a new micropost |
POST |
/microposts |
create |
create a new micropost |
GET |
/microposts/1/edit |
edit |
page to edit micropost with id 1 |
PATCH |
/microposts/1 |
update |
update micropost with id 1 |
DELETE |
/microposts/1 |
destroy |
delete micropost with id 1 |
The Microposts controller itself appears in schematic form in Listing 2.9. Note that, apart from MicropostsController in replacing of UsersController, Listing 2.9 is identical to the code in Listing 2.4. This is a reflection of the REST architecture common to both resources.
Listing 2.9 The Microposts controller in schematic form.
app/controllers/microposts_controller.rb
____________________________________________________________________________
class MicropostsController < ApplicationController
.
.
.
def index
.
.
.
end
def show
.
.
.
end
def new
.
.
.
end
def edit
.
.
.
end
def create
.
.
.
end
def update
.
.
.
end
def destroy
.
.
.
end
end
____________________________________________________________________________
To make some actual microposts, we enter information at the new microposts page, /microposts/new, as seen in Figure 2.12.
Figure 2.12 The new micropost page (/microposts/new).
At this point, go ahead and create a micropost or two, taking care to make sure that at least one has a user_id of 1 to match the ID of the first user created in Section 2.2.1. The result should look something like Figure 2.13.
Figure 2.13 The micropost index page (/microposts).
2.3.2 Putting the Micro in Microposts
Any micropost worthy of the name should have some means of enforcing the limits on the length of the post. Implementing this constraint in Rails is easy with validations; to accept microposts with at most 140 characters (à la Twitter), we use a length validation. At this point, you should open the file app/models/micropost.rb in your text editor or IDE and fill it with the contents of Listing 2.10.
Listing 2.10 Constraining microposts to be at most 140 characters.
app/models/micropost.rb
____________________________________________________________________________
class Micropost < ActiveRecord::Base
validates :content, length: { maximum: 140 }
end
____________________________________________________________________________
The code in Listing 2.10 may look rather mysterious—we’ll cover validations more thoroughly starting in Section 6.2—but its effects are readily apparent if we go to the new micropost page and enter more than 140 characters for the content of the post. As seen in Figure 2.14, Rails renders error messages indicating that the micropost’s content is too long. (We’ll learn more about error messages in Section 7.3.3.)
Figure 2.14 Error messages for a failed micropost creation.
2.3.3 A User has_many Microposts
One of the most powerful features of Rails is the ability to form associations between different data models. In the case of our User model, each user potentially has many microposts. We can express this relationship in code by updating the User and Micropost models as in Listing 2.11 and Listing 2.12, respectively.
Listing 2.11 A user has many microposts.
app/models/user.rb
____________________________________________________________________________
class User < ActiveRecord::Base
has_many :microposts
end
____________________________________________________________________________
Listing 2.12 A micropost belongs to a user.
app/models/micropost.rb
____________________________________________________________________________
class Micropost < ActiveRecord::Base
belongs_to :user
validates :content, length: { maximum: 140 }
end
____________________________________________________________________________
We can visualize the result of this association in Figure 2.15. Because of the user_id column in the microposts table, Rails (using Active Record) can infer the microposts associated with each user.
Figure 2.15 The association between microposts and users.
In Chapter 11 and Chapter 12, we will use the association of users and microposts both to display all of a user’s microposts and to construct a Twitter-like micropost feed. For now, we can examine the implications of the user—micropost association by using the console, which is a useful tool for interacting with Rails applications. We first invoke the console with rails console at the command line, and then retrieve the first user from the database using User.first (putting the results in the variable first_user):10
$ rails console
<< first_user = User.first
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.org",
created_at: "2014-07-21 02:01:31", updated_at: "2014-07-21 02:01:31">
>> first_user.microposts
=> [#<Micropost id: 1, content: "First micropost!", user_id: 1, created_at:
"2014-07-21 02:37:37", updated_at: "2014-07-21 02:37:37">, #<Micropost id: 2,
content: "Second micropost", user_id: 1, created_at: "2014-07-21 02:38:54",
updated_at: "2014-07-21 02:38:54">]
>> micropost = first_user.microposts.first # Micropost.first would also work.
=> #<Micropost id: 1, content: "First micropost!", user_id: 1, created_at:
"2014-07-21 02:37:37", updated_at: "2014-07-21 02:37:37">
>> micropost.user
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.org",
created_at: "2014-07-21 02:01:31", updated_at: "2014-07-21 02:01:31">
>> exit
(I include exit in the last line just to demonstrate how to exit the console. On most systems, you can also use Ctrl-D for the same purpose.11) Here we have accessed the user’s microposts using the code first_user.microposts. With this code, Active Record automatically returns all the microposts with user_id equal to the ID of first_user (in this case, 1). We’ll learn much more about the association facilities in Active Record in Chapter 11 and Chapter 12.
2.3.4 Inheritance Hierarchies
We end our discussion of the toy application with a brief description of the controller and model class hierarchies in Rails. This discussion will make sense only if you have some experience with object-oriented programming (OOP); if you haven’t studied OOP, feel free to skip this section. In particular, if you are unfamiliar with classes (discussed in Section 4.4), you might want to loop back to this section at a later time.
We start with the inheritance structure for models. Comparing Listing 2.13 and Listing 2.14, we see that both the User model and the Micropost model inherit (via the left angle bracket <) from ActiveRecord::Base, which is the base class for models provided by Active Record; a diagram summarizing this relationship appears in Figure 2.16. It is by inheriting from ActiveRe\-cord::\-Base that our model objects gain the ability to communicate with the database, treat the database columns as Ruby attributes, and so on.
Listing 2.13 The User class, highlighting inheritance.
app/models/user.rb
____________________________________________________________________________
class User < ActiveRecord::Base
.
.
.
end
____________________________________________________________________________
Listing 2.14 The Micropost class, highlighting inheritance.
app/models/micropost.rb
____________________________________________________________________________
class Micropost < ActiveRecord::Base
.
.
.
end
____________________________________________________________________________
Figure 2.16 The inheritance hierarchy for the User and Micropost models.
The inheritance structure for controllers is only slightly more complicated. Comparing Listing 2.15 and Listing 2.16, we see that both the Users controller and the Microposts controller inherit from the Application controller. Examining Listing 2.17, we see that ApplicationController itself inherits from ActionController::Base; this is the base class for controllers provided by the Rails library Action Pack. The relationships between these classes are illustrated in Figure 2.17.
Listing 2.15 The UsersController class, highlighting inheritance.
app/controllers/users_controller.rb
____________________________________________________________________________
class UsersController < ApplicationController
.
.
.
end
____________________________________________________________________________
Listing 2.16 The MicropostsController class, highlighting inheritance.
app/controllers/microposts_controller.rb
____________________________________________________________________________
class MicropostsController < ApplicationController
.
.
.
end
____________________________________________________________________________
Listing 2.17 The ApplicationController class, highlighting inheritance.
app/controllers/application_controller.rb
____________________________________________________________________________
class ApplicationController < ActionController::Base
.
.
.
end
____________________________________________________________________________
Figure 2.17 The inheritance hierarchy for the Users and Microposts controllers.
As with model inheritance, both the Users and Microposts controllers gain a large amount of functionality by inheriting from a base class (in this case, ActionController::Base), including the ability to manipulate model objects, filter inbound HTTP requests, and render views as HTML. Given that all Rails controllers inherit from ApplicationController, rules defined in the Application controller automatically apply to every action in the application. For example, in Section 8.4 we’ll see how to include helpers for logging in and logging out of all of the sample application’s controllers.
2.3.5 Deploying the Toy App
With the completion of the Microposts resource, now is a good time to push the repository up to Bitbucket:
$ git status
$ git add -A
$ git commit -m "Finish toy app"
$ git push
Ordinarily, you should make smaller, more frequent commits, but for the purposes of this chapter a single big commit at the end is fine.
At this point, you can also deploy the toy app to Heroku as explained in Section 1.5:
$ git push heroku
(This assumes you created the Heroku app in Section 2.1. Otherwise, you should run heroku create and then git push heroku master.)
To get the application’s database to work, you’ll also have to migrate the production database:
$ heroku run rake db:migrate
This updates the database at Heroku with the necessary user and micropost data models. After running the migration, you should be able to use the toy app in production, with a real PostgreSQL database back-end (Figure 2.18).
Figure 2.18 Running the toy app in production.