Appendix A. Solutions

Chapter 1

  1. Exercise 1 is about learning to read a file and exploring the array methods using the contents of the file. I’d expect to see something like this in the console after completing the exercise:

    irb(main):001:0> file = File.read("test.txt")
     => "Call me Ishmael..."
    irb(main):002:0> puts file.split
    Call
    me
    Ishmael
    --snip--
     => nil
    irb(main):003:0> puts file.split.length
     => 198
    irb(main):004:0> puts file.split.uniq.length
     => 140

    The output depends on the text you used.

  2. The second exercise requires writing a little code. The following sample solves the problem using only methods covered so far:

    file = File.read("test.txt")
    counts = {}
    file.split.each do |word|
      if counts[word]
        counts[word] = counts[word] + 1
      else
        counts[word] = 1
      end
    end
    puts counts

    This solution should print something like this:

    => {"Call"=>1, "me"=>3, "Ishmael."=>1, ...

    The word “Call” appears once in the paragraph; the word “me” appears three times; and so on.

  3. Using the sample code provided in Exercise 3, the complete solution looks like this:

    class WordCounter
      def initialize(file_name)
        @file = File.read(file_name)
      end
    
      def count
        @file.split.length
      end
    
      def uniq_count
        @file.split.uniq.length
      end
    
      def frequency
        counts = {}
        @file.split.each do |w|
          if counts[w]
            counts[w] = counts[w] + 1
          else
            counts[w] = 1
          end
        end
      end
    end

This combines the solutions to the first two exercises, wrapping them in a Ruby class.

Chapter 2

  1. The first exercise is about familiarizing yourself with a simple Rails application and the functionality provided by default. The address of the home page is http://localhost:3000/posts. As you move around the application, that address changes. The new post form is at /posts/new; the first post is at /posts/1; and the form for editing the first post is at /posts/1/edit. These paths and their meaning are covered in Chapter 4.

  2. If you’ve never worked on a large application before, the number of files in a typical Rails application can seem daunting. Most editors contain some type of project list for opening files, as well as keyboard shortcuts for quickly searching for files by name. These features are invaluable when working on larger projects.

Chapter 3

  1. The following commands generate and run the migration to add an email address to comments:

    $ bin/rails g migration add_email_to_comments email:string
        invoke  active_record
        create    db/migrate/20140404225418_add_email_to_comments.rb
    $ bin/rake db:migrate
    == 20140404225418 AddEmailToComments: migrating...
    --snip--

    You can then launch a Rails console with bin/rails console and create a new comment with an email address.

  2. Open app/models/comment.rb and add the validation as shown here:

    class Comment < ActiveRecord::Base
      belongs_to :post
      validates :author, :body, presence: true
    end

    Note that I added the validation for both fields on a single line. You could do this, however, with two separate calls to the validates method.

  3. You can’t write a single query to determine the number of comments for each post, but you can iterate over all posts and count the comments. Enter something like this in the Rails console:

    2.1.0 :001 > Post.all.each do |post|
    2.1.0 :002 *   puts post.comments.count
    2.1.0 :003 > end

This code first finds all of the posts and then makes a count query on the comments table for each one.

Chapter 4

  1. Open the file app/controllers/comments_controller.rb, and find the create method.

    class CommentsController < ApplicationController
      def create
        @post = Post.find(params[:post_id])
    
        if @post.comments.create(comment_params) ➊
          redirect_to @post,
                      notice: 'Comment was successfully created.'
        else
          redirect_to @post,
                      alert: 'Error creating comment.'
        end
      end
    --snip--

    Note that it currently uses @post.comments.create(comment_params) ➊ to initialize and save the new comment as part of the if statement. You need to store the new comment in a variable so you can use the errors method to get a list of errors when the save fails. Update the create method as shown here:

    class CommentsController < ApplicationController
      def create
        @post = Post.find(params[:post_id])
        @comment = @post.comments.build(comment_params)
    
        if @comment.save
          redirect_to @post,
                      notice: 'Comment was successfully created.'
        else
          redirect_to @post,
                      alert: 'Error creating comment. ' +
                        @comment.errors.full_messages.to_sentence ➊
        end
      end
    --snip--

    This code adds the errors to the existing alert. Notice I used the to_sentence method ➊ to convert the array of error messages to a sentence like this: “Author can’t be blank and Body can’t be blank.”

  2. Edit app/controllers/comments_controller.rb, and find the comment_params method. Add :email to the call to the permit method:

    class CommentsController < ApplicationController
    --snip--
    
      private
      def comment_params
        params.require(:comment).permit(:author, :body, :email)
      end
    end

Now if a user enters an email address when adding a new comment, the address should be stored in the database. Without this change, the email field is simply ignored.

Chapter 5

  1. Remove the h1 element from app/views/posts/index.html.erb and update app/views/layouts/application.html.erb, as shown here:

    --snip--
    <body>
    <h1>Listing posts</h1>
    
    <%= yield %>
    
    </body>
    </html>

    Also change the headings in app/views/posts/new.html.erb and app/ views/posts/edit.html.erb to h2 headings:

    <h2>New post</h2>
    
    <%= render 'form' %>
    
    <%= link_to 'Back', posts_path %>
  2. First, add a label and text field for :author to the app/views/posts/ _form.html.erb partial:

    --snip--
      <div class="field">
        <%= f.label :title %><br>
        <%= f.text_field :title %>
      </div>
      <div class="field">
        <%= f.label :author %><br>
        <%= f.text_field :author %>
      </div>
      <div class="field">
        <%= f.label :body %><br>
        <%= f.text_area :body %>
      </div>
    --snip--

    Then add :author to the list of permitted parameters in the post_ params method at the bottom of app/controllers/posts_controller.rb:

    --snip--
        def post_params
          params.require(:post).permit(:title, :author, :body)
        end
    end
  3. Make the changes to config/routes.rb and app/views/comments/_comment.html.erb as described in the question. Here is how I would write the destroy action in app/controllers/comments_controller.rb:

    --snip--
      def destroy
        @post = Post.find(params[:post_id])
        @comment = @post.comments.find(params[:id])
    
        @comment.destroy
        respond_to do |format|
          format.html { redirect_to @post }
          format.json { head :no_content }
        end
      end
    --snip--

Chapter 6

  1. After editing files in your application, stage your changes in Git with git add ., then commit these changes with git commit -m " Commit Message", and finally push the changes to Heroku with git push heroku master.

  2. If you don’t already have a GitHub account, go to https://github.com/ and complete the sign-up form. Next you’ll need to choose a plan. The free plan includes unlimited public repositories. Once you finish the sign-up process, you should see the GitHub Bootcamp screen. Follow the instructions there to create a repository and upload your application.

  3. Create your new application in the code directory you created in Chapter 2, not inside the blog directory. Use the rails new command followed by the name of your new application. For example, to create an application to track your record collection, type this command:

    $ rails new vinyl

    Next think about the models your application needs. In this case, you probably need a Record or Album model. The model needs fields such as title, artist, and release_date. Move to the vinyl directory, and use the rails scaffold command to generate some code to get started:

    $ cd vinyl
    $ bin/rails generate scaffold Album title artist release_date:datetime

Now start the Rails server and work with your new application.

Chapter 7

  1. In my version of Rails, the Post class has 58 ancestors.

    irb(main):001:0> Post.ancestors.count
    => 58

    Using the Ruby pretty-print method (pp), you can list each ancestor on a separate line:

    irb(main):012:0> pp Post.ancestors
    [Post(id: integer, title: string, body: text, created_at: datetime,
    updated_at: datetime, author: string),
     Post::GeneratedFeatureMethods,
     #<Module:0x007fabc21bafd8>,
     ActiveRecord::Base,
     --snip--
     ActiveRecord::Validations,
     --snip--
     Kernel,
     BasicObject]

    As you scroll through the list of ancestors, you should see some names you recognize, such as ActiveRecord::Associations and ActiveRecord::Validations. Also, notice that Post inherits from BasicObject, just like every other class in Ruby.

  2. The cannot_feature! method should be the same as the can_feature! method except it assigns false to the @features[f] instead of true.

    class User
      FEATURES = ['create', 'update', 'delete']
    
      FEATURES.each do |f|
        define_method "can_#{f}!" do
          @features[f] = true
        end
    
        define_method "cannot_#{f}!" do
          @features[f] = false
        end
    
        define_method "can_#{f}?" do
          !!@features[f]
        end
      end
    
      def initialize
        @features = {}
      end
    end

    After adding this method, create another instance of the User class and make sure the new method works as expected:

    irb(main):001:0> user = User.new
     => #<User:0x007fc01b95abe0 @features={}>
    irb(main):002:0> user.can_create!
     => true
    irb(main):003:0> user.can_create?
     => true
    irb(main):004:0> user.cannot_create!
     => false
    irb(main):005:0> user.can_create?
     => false
  3. First, look at the instance methods defined by the Element class:

    irb(main):001:0> Element.instance_methods(false)
     => [:name, :name=]

    The methods name and name= are defined as expected. Now reopen the Element class and add a call to accessor :symbol:

    irb(main):002:0> class Element
    irb(main):003:1> accessor :symbol
    irb(main):004:1> end
     => :symbol=

    This should create two new methods named symbol and symbol=. You can verify that the methods were created by calling instance_methods again:

    irb(main):005:0> Element.instance_methods(false)
     => [:name, :name=, :symbol, :symbol=]

You can verify that the methods work as expected by creating an instance of the Element class and assigning a symbol with e.symbol = "Au".

Chapter 8

  1. Specifying dependent: :destroy on the belongs_to side of the association causes the parent model to be destroyed when any child model is destroyed. In this example, destroying any Post also destroys the associated User. This mistake is fairly common.

  2. The completed Comment model should look like this:

    class Comment < ActiveRecord::Base
      belongs_to :post
      belongs_to :user
    
      validates :post_id, presence: true
      validates :user_id, presence: true
    end

    The Rails generator adds belongs_to associations automatically, but it does not add validations.

  3. Launch the Rails console with bin/rails console. Create a new User, TextPost, and Comment. Verify that all of the models were created. Then call destroy on the new User and verify that the associated TextPost and Comment records are also destroyed.

    irb(main):001:0> carol = User.create name: "Carol"
     => #<User id: 3, name: "Carol", ...>
    irb(main):002:0> post = TextPost.create user: carol, body: "Testing"
     => #<TextPost id: 3, body: "Testing", ...>
    irb(main):003:0> comment = Comment.create post: post, user: carol, 
                                    body: "Hello"
     => #<Comment id: 1, body: "Hello", ...>
    irb(main):004:0> carol.posts.count
     => 1
    irb(main):005:0> carol.comments.count
     => 1
    irb(main):006:0> carol.destroy ➊
    --snip--
     => #<User id: 3, name: "Carol", ...>
    irb(main):007:0> carol.posts.count
     => 0
    irb(main):008:0> carol.comments.count
     => 0
    irb(main):009:0> carol.reload ➋
    ActiveRecord::RecordNotFound: Couldn't find User with id=3
    --snip--

Note that calling destroy on the model does not remove it from memory ➊. The variable carol still refers to the model even though it has been deleted from the database. Attempting to reload the model from the database raises an ActiveRecord::RecordNotFound exception because the record for carol has been deleted ➋.

Chapter 9

  1. First, edit the text post partial at app/views/text_posts/_text_post.html.erb, as shown here:

    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 class="panel-title">
          <%= text_post.title %>
        </h3>
        <%= link_to(
              "#{time_ago_in_words text_post.created_at} ago",
              post_path(text_post)) %>
      </div>
    --snip--

    This creates a link to the text_post with the time in words such as “5 days ago.” Edit the image post partial at app/views/image_posts/ _image_post.html.erb with a similar change.

    --snip--
      </h3>
      <%= link_to "#{time_ago_in_words image_post.created_at} ago",
            post_path(image_post) %>
    </div>
    --snip--

    The only difference here is the word text_post is replaced with image_post. Now load the posts index page and make sure the links work correctly.

  2. The most important part of this exercise is restricting access to the controller to authenticated users. Add before_action :authenticate_user! in app/controllers/comments_controller.rb, as shown here:

    class CommentsController < ApplicationController
      before_action :authenticate_user!
    
      --snip--
    end

    The comment partial at app/views/comments/_comment.html.erb shows the name of the user that created the comment and the body of the comment.

    <p><em><%= comment.user.name %> said:</em></p>
    <p><%= comment.body %></p>

    This partial is rendered once for each comment by render @post.comments in the post show view.

  3. First, start a Rails console with bin/rails console to see the password_digest for a user.

    irb(main):001:0> alice = User.find 1
      User Load ...
     => #<User id: 1, name: "Alice", ...>
    irb(main):002:0> alice.password_digest
     => "$2a$10$NBjrpHtfLJN14c6kVjG7sety1N4ifyuto7GD5qX7xHdVmbtweL1Ny"

The value of alice.password_digest that you see will be different. Bcrypt automatically adds a salt to the password before generating the hash digest. I can’t tell the password for alice by looking at that value. Bcrypt seems pretty secure!

You can see the cookies for a site by looking at resources in your browser’s Developer Tools or Page Info. According to the Chrome developer tools, my current _social_session cookie is 465 bytes of alphanumeric digits like this "M2xkVmNTaGpVaFd...". Again, I’m not able to decipher that information.

Chapter 10

  1. Open the TextPost partial at app/views/text_posts/_text_post.html.erb. It already displays the user’s name. Add a call to the link_to helper method before the text_post.user.name and also pass the text_post.user to the helper:

    --snip--
    <div class="panel-body">
      <p><em>By <%= link_to text_post.user.name, text_post.user %></em></p>
    
      <%= text_post.body %>
    </div>
    --snip--

    Then update the ImagePost partial at app/views/image_posts/_image _post.html.erb:

    --snip--
    <div class="panel-body">
      <p><em>By <%= link_to image_post.user.name, image_post.user %></em></p>
    
      <%= image_tag image_post.url, class: "img-responsive" %>
    
      <%= image_post.body %>
    </div>
    --snip--

    Finally, update the application layout at app/views/layouts/application.html.erb:

    --snip--
    <div class="pull-right">
      <% if current_user %>
        <%= link_to 'Profile', current_user %>
        <%= link_to 'Log Out', logout_path %>
      <% else %>
    --snip--

    The application layout already has a check for current_user. Add the Profile link inside this conditional.

  2. Open UsersController at app/controllers/users_controller.rb. Requiring authentication before the follow action is a one-line change using the authenticate_user! method you wrote in Chapter 9.

    class UsersController < ApplicationController
      before_action :authenticate_user!, only: :follow
    
    --snip--

    The only: :follow option means anonymous users can still access the show, new, and create actions. Now update the user show view at app/ views/users/show.html.erb. I used two if statements to first verify that current_user is not nil, and then to verify that current_user is not equal to or already following the user being displayed.

    --snip--
    <p class="lead"><%= @user.name %></p>
    
    <% if current_user %>
      <% if current_user != @user && !current_user.following?(@user) %>
        <%= link_to "Follow", follow_user_path(@user),
                 class: "btn btn-default" %>
      <% end %>
    <% end %>
    
    <h3>Posts</h3>
    --snip--

    You could have also done this with a single if combining all three of the conditional statements.

  3. First, open app/controllers/image_posts_controller.rb, and add methods for the new and create actions and the private image_post_params method. These are similar to the corresponding methods in TextPostsController.

    class ImagePostsController < ApplicationController
      def new
        @image_post = ImagePost.new
      end
      def create
        @image_post = current_user.image_posts.build(image_post_params)
        if @image_post.save
          redirect_to post_path(@image_post),
                        notice: "Post created!"
        else
          render :new, alert: "Error creating post."
        end
      end
    
      private
    
      def image_post_params
        params.require(:image_post).permit(:title, :url, :body)
      end
    end

    Next, add the new view at app/views/image_posts/new.html.erb:

    <div class="page-header">
      <h1>New Image Post</h1>
    </div>
    
    <%= render 'form' %>

    Then add the form partial at app/views/image_posts/_form.html.erb:

    <%= form_for @image_post do |f| %>
      <div class="form-group">
        <%= f.label :title %>
        <%= f.text_field :title, class: "form-control" %>
      </div>
      <div class="form-group">
        <%= f.label :url %>
        <%= f.text_field :url, class: "form-control" %>
      </div>
      <div class="form-group">
        <%= f.label :body %>
        <%= f.text_area :body, class: "form-control" %>
      </div>
    
      <%= f.submit class: "btn btn-primary" %>
      <%= link_to 'Cancel', :back, class: "btn btn-default" %>
    <% end %>

    Finally, add a button to the home page at app/views/posts/index.html. erb that links to the New Image Post form:

    --snip--
    <p>
      <%= link_to "New Text Post", new_text_post_path,
            class: "btn btn-default" %>
      <%= link_to "New Image Post", new_image_post_path,
            class: "btn btn-default" %>
    </p>
    --snip--

Refer back to Create Post if you have any questions about these actions or views.

Chapter 11

  1. First, add methods for the edit and update actions to the ImagePostsController at app/controllers/image_posts_controller.rb, as shown here:

    --snip--
    
    def edit
    
      @image_post = current_user.image_posts.find(params[:id])
    end
    
      def update
        @image_post = current_user.image_posts.find(params[:id])
        if @image_post.update(image_post_params)
          redirect_to post_path(@image_post), notice: "Post updated!"
        else
          render :edit, alert: "Error updating post."
        end
      end
    
      private
    
      def image_post_params
        params.require(:image_post).permit(:title, :body, :url)
      end
    end

    Next, create the edit view at app/views/image_posts/edit.html.erb:

    <div class="page-header">
      <h1>Edit Image Post</h1> </div>
    <%= render 'form' %>

    This view uses the form partial you created in Chapter 10. Finally, add a link to the edit action in the ImagePost partial at app/views/image _posts/_image_post.html.erb:

    --snip--
    <%= image_post.body %>
        <% if image_post.user == current_user %>
          <p>
          <%= link_to 'Edit', edit_image_post_path(image_post),
                class: "btn btn-default" %>
          </p>
        <% end %>
      </div>
    </div>

    This link is wrapped in a conditional so it only appears if this image post was created by the current user.

  2. Update the PostsController at app/controllers/posts_controller.rb, as shown in the question.

      --snip--
    
      def show
        @post = Post.find(params[:id])
        @can_moderate = (current_user == @post.user)
      end
    end

    Now edit the comment partial at app/views/comments/_comment.html.erb and add a link to destroy the comment when the @can_moderate instance variable is true:

    <p><em><%= comment.user.name %> said:</em></p>
    <p><%= comment.body %></p>
    <% if @can_moderate %>
      <p>
      <%= link_to 'Destroy', comment_path(comment),
            method: :delete, class: "btn btn-default" %>
      </p>
    <% end %>

    Be sure to add method: :delete to the link so the destroy action is called. Finally, add the destroy action to the CommentsController at app/ controllers/comments_controller.rb:

    --snip--
    
    def destroy
      @comment = Comment.find(params[:id])
    
      if @comment.destroy
        redirect_to post_path(@comment.post_id),
                    notice: 'Comment successfully destroyed.'
      else
        redirect_to post_path(@comment.post_id),
                    alert: 'Error destroying comment.'
      end
    end
      private
    
      def comment_params
        params.require(:comment).permit(:body, :post_id)
      end
    end

    This method finds the comment, calls destroy, and redirects back to the post with a message indicating success or failure.

  3. Open the routes file at config/routes.rb and edit at the logout route:

      --snip--
      get 'login', to: 'sessions#new', as: 'login'
      delete 'logout', to: 'sessions#destroy', as: 'logout'
    
      root 'posts#index'
    end

    Edit the application layout at app/views/layouts/application.html.erb and add method: :delete to the Log Out link.

    --snip--
    
    <div class="pull-right">
      <% if current_user %>
        <%= link_to 'Profile', current_user %>
        <%= link_to 'Log Out', logout_path, method: :delete %>
      <% else %>
    --snip--

Now the link issues a DELETE request to log out of the application.

Chapter 12

  1. The show page loads the collection of comments to render and then loads the owner of each comment individually as the comments are rendered. You can eager load the comments and the owners for a post by adding includes(comments: [:user]) in the show method in the PostsController at app/controllers/posts_controller.rb:

    --snip--
    
      def show
        @post = Post.includes(comments: [:user]).find(params[:id]) ➊
        @can_moderate = (current_user == @post.user)
      end
    end

    Adding includes(comments: [:user]) tells Rails to eager load the comments for this post and all users associated with those comments.

  2. Open the Comment partial at app/views/comments/_comment.html.erb and add the cache block:

    <% cache [comment, @can_moderate] do %> ➊
      <p><em><%= comment.user.name %> said:</em></p>
      <p><%= comment.body %></p>
      <% if @can_moderate %>
        <p>
          <%= link_to 'Destroy', comment_path(comment),
                method: :delete, class: "btn btn-default" %>
        </p>
      <% end %>
    <% end %>

    Passing an array to the cache method creates a cache key that combines the elements in the array ➊. In this case, the cache key contains the values of the comment’s id and updated_at fields and the value of @can_moderate, either true or false.

  3. Open the show page at app/views/posts/show.html.erb and add the cache block.

    --snip--
    
    <h3>Comments</h3>
    <% cache [@post, 'comments', @can_moderate] do %> ➊
      <%= render @post.comments %>
    <% end %>
    
    --snip--

    This creates a cache key that is a combination of the cache key for @post, the word “comments,” and the value of @can_moderate ➊. Now the comments collection is displayed after a single read from the cache.

Chapter 13

  1. You need to update the view partials for both types of posts for this exercise. First, edit the file app/views/text_posts/_text_post.html.erb and add a debug call near the bottom, as shown here:

    <div class="panel panel-default">
      --snip--
    
        <%= debug text_post %>
      </div>
    </div>

    Then edit app/views/link_posts/_link_post.html.erb and add a debug call near the bottom:

    <div class="panel panel-default">
      --snip--
    
        <%= debug link_post %>
      </div>
    </div>
  2. The easiest way to add the id and type of each post to the log is by iterating over the contents of the @posts instance variable. Edit app/controllers/ posts_controller.rb and update the index action.

    class PostsController < ApplicationController
      before_action :authenticate_user!
    
      def index
        user_ids = current_user.timeline_user_ids
        @posts = Post.includes(:user).where(user_id: user_ids)
                   .paginate(page: params[:page], per_page: 5)
                   .order("created_at DESC")
    
        @posts.each do |post|
          logger.debug "Post #{post.id} is a #{post.type}"
        end
      end
    --snip--

    Now when you refresh the posts index page, you should see five lines similar to “Post 5 is a TextPost” in the log.

  3. To debug what happens when a user logs in to the application, you need to add a debugger call to the create action in app/controllers/ sessions_controller.rb:

    class SessionsController < ApplicationController
      --snip--
    
      def create
        debugger
        user = User.find_by(email: params[:email])
        if user && user.authenticate(params[:password])
          session[:user_id] = user.id
          redirect_to root_url, :notice => "Logged in!"
        else
          flash.now.alert = "Invalid email or password"
          render "new"
        end
      end
    
      --snip--

With this line in place, you can examine the params sent to this action, the current contents of the session, and the value of user as you move through this action.

Chapter 14

  1. This curl command is the same one you used earlier to create a new post, except I replaced the token with the word fake.

    $ curl -i 
           -d '{"text_post":{"title":"Test","body":"Hello"}}' 
           -H "Content-Type: application/json" 
           -H "Authorization: Token fake" 
           http://localhost:3000/api/text_posts
    
    HTTP/1.1 401 Unauthorized
    --snip--
    
    HTTP Token: Access denied.

    Note that the status code is 401 Unauthorized and the body contains the text "HTTP Token: Access denied."

  2. Text posts validate the presence of a body, so use curl to attempt to create a text post without specifying a body.

    $ curl -i 
           -d '{"text_post":{"title":"Test"}}' 
           -H "Content-Type: application/json" 
           -H "Authorization: Token token" 
           http://localhost:3000/api/text_posts
    HTTP/1.1 422 Unprocessable Entity
    --snip--
    
    {"errors":{"body":["can't be blank"]}}

    Note that the status code is 422 Unprocessable Entity and the body contains a JSON representation of the errors.

  3. Add the show method to app/controllers/api/posts_controller.rb:

    module Api
      class PostsController < ApplicationController
        respond_to :json
    
        --snip--
    
        def show
          @post = Post.find(params[:id])
          respond_with @post
        end
      end
    end

    This method finds the requested post and assigns it to the @post instance variable and then responds with that post. The following curl command verifies that this action is working:

    $ curl http://localhost:3000/api/posts/1
    {
      "id":1,
      "title":"First Post",
      "body":"Hello, World!",
      "url":null,
      "user_id":1,
      "created_at":"2014-04-22T00:56:48.188Z",
      "updated_at":"2014-04-22T00:56:48.188Z"
    }

Because you didn’t create a jbuilder view for this action, the default JSON representation for posts is returned.

Chapter 15

  1. Edit the file app/views/layouts/application.html.erb to change the title of each page:

    <!DOCTYPE html>
    <html>
    <head>
      <title>My Awesome Site</title>
      --snip--

    After you save this change, add it to your local Git repositories staging area, and then commit the change with an appropriate commit message.

    $ git add .
    $ git commit -m "Update title"

    Now deploy your change by entering bin/cap production deploy in your terminal.

  2. The Ruby Toolbox at https://www.ruby-toolbox.com/ lists hundreds of gems you can use to add features to your application. For example, you can let users upload files to your application. Check the Rails File Uploads category to find several choices, including Paperclip and CarrierWave. From there, you can visit the website, read the documentation, and see the source code for each project.

  3. Go to https://github.com/rails/rails/ to join the discussion on open issues and pull requests, and see previous commits. Ruby on Rails has a page at http://rubyonrails.org/community/ for those looking to get involved online. You can learn about upcoming Ruby and Rails conferences at http://rubyconf.org/ and http://railsconf.com,/ respectively. I hope to see you there!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.111.197