Home > Articles > Programming > Ruby

This chapter is from the book

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

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

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

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

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

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

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

Figure 2.18 Running the toy app in production.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020