Rails Crash Course A No Nonsense Guide To Development (2015)
User Manual:
Open the PDF directly: View PDF
.
Page Count: 298
| Download | |
| Open PDF In Browser | View PDF |
Rails Crash Course
Rails Crash Course. Copyright © 2015 by Anthony Lewis.
All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording, or by any information storage or retrieval
system, without the prior written permission of the copyright owner and the publisher.
Printed in USA
First printing
18 17 16 15 14 1 2 3 4 5 6 7 8 9
ISBN-10: 1-59327-572-2
ISBN-13: 978-1-59327-572-3
Publisher: William Pollock
Production Editor: Serena Yang
Cover Illustration: W. Sullivan
Interior Design: Octopod Studios
Developmental Editor: Jennifer Griffith-Delgado
Technical Reviewer: Xavier Noria
Copyeditor: LeeAnn Pickrell
Compositor: Susan Glinert Stevens
Proofreader: James Fraleigh
Indexer: Nancy Guenther
For information on distribution, translations, or bulk sales, please contact No Starch Press, Inc. directly:
No Starch Press, Inc.
245 8th Street, San Francisco, CA 94103
phone: 415.863.9900; info@nostarch.com
www.nostarch.com
Library of Congress Cataloging-in-Publication Data
Lewis, Anthony, 1975- author.
Rails crash course : a no-nonsense guide to Rails development / by Anthony Lewis.
pages cm
ISBN 978-1-59327-572-3 -- ISBN 1-59327-572-2
1. Ruby (Computer program language) 2. Ruby on rails (Electronic resource) I. Title.
TK5105.8885.R83L49 2015
006.7'54--dc23
2014034816
No Starch Press and the No Starch Press logo are registered trademarks of No Starch Press, Inc. Other
product and company names mentioned herein may be the trademarks of their respective owners. Rather
than use a trademark symbol with every occurrence of a trademarked name, we are using the names only
in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the
trademark.
The information in this book is distributed on an “As Is” basis, without warranty. While every precaution
has been taken in the preparation of this work, neither the author nor No Starch Press, Inc. shall have any
liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or
indirectly by the information contained in it.
Brief Contents
Foreword by Xavier Noria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix
Part I: Ruby on Rails Fundamentals
Chapter 1: Ruby Fundamentals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Chapter 2: Rails Fundamentals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Chapter 3: Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Chapter 4: Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Chapter 5: Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Chapter 6: Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Part II: Building a Social Networking App
Chapter 7: Advanced Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Chapter 8: Advanced Active Record . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Chapter 9: Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Chapter 10: Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Chapter 11: Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Chapter 12: Performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
Chapter 13: Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
Chapter 14: Web APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
Chapter 15: Custom Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
Conte nt s in De ta il
Foreword by Xavier Noria
xv
Acknowledgments
xvii
Introduction
xix
Who This Book Is For . . . . . . . .
Overview . . . . . . . . . . . . . . . . .
Installation . . . . . . . . . . . . . . . .
Ruby, Rails, and Git . . .
Multiple Ruby Versions .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. xx
xx
xxi
xxi
xxiii
Part I
Ruby on Rails Fundamentals
1
Ruby Fundamentals
Interactive Ruby . . . . . .
Data Types . . . . . . . . . .
Numbers . . . . .
Strings . . . . . .
Symbols . . . . .
Arrays . . . . . .
Hashes . . . . . .
Booleans . . . . .
Constants . . . . . . . . . . .
Variables . . . . . . . . . . .
Control Flow . . . . . . . . .
Conditionals . .
Iteration . . . . .
Methods . . . . . . . . . . .
Classes . . . . . . . . . . . .
Class Methods .
Inheritance . . .
Summary . . . . . . . . . . .
Exercises . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
Rails Fundamentals
Your First Rails Application . . . . . . . . . . .
Rails Principles . . . . . . . . . . . . . . . . . . .
Convention over Configuration .
Don’t Repeat Yourself . . . . . . . .
. 4
. 5
. 5
. 6
. 7
. 7
. 8
. 9
10
10
11
11
12
14
15
17
17
18
18
19
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
20
21
21
22
Rails Architecture . . . . . . . . . . .
Model . . . . . . . . . . . .
View . . . . . . . . . . . . .
Controller . . . . . . . . . .
Rails Application Structure . . . . .
The app Directory . . . .
The bin Directory . . . . .
The config Directory . . .
The db Directory . . . . .
The lib Directory . . . . .
The log Directory . . . . .
The public Directory . . .
The test Directory . . . . .
The tmp Directory . . . . .
The vendor Directory . .
Rails Commands . . . . . . . . . . . .
The gem Command . . .
The bundle Command .
The rake Command . . .
The rails Command . . .
Rails Scaffold . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
Models
The Post Model . . . . . . . . . . . . . . . .
Active Record . . . . . . . . . . . . . . . . .
Create, Read, Update, and Delete . .
Create . . . . . . . . . . . . . . .
Read . . . . . . . . . . . . . . . .
Update . . . . . . . . . . . . . . .
Delete . . . . . . . . . . . . . . . .
More Active Record Methods . . . . . .
Query Conditions . . . . . . . .
Calculations . . . . . . . . . . .
Migrations . . . . . . . . . . . . . . . . . . .
The Schema . . . . . . . . . . .
Adding a Column . . . . . . .
Inside the Author Migration .
Validations . . . . . . . . . . . . . . . . . . .
Adding a Validation . . . . . .
Testing Data . . . . . . . . . . .
Associations . . . . . . . . . . . . . . . . . .
Generating the Model . . . .
Adding Associations . . . . . .
Using Associations . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . . . . .
viii
Contents in Detail
22
22
22
23
23
23
23
23
24
24
24
24
25
25
25
25
25
26
26
26
27
28
28
29
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
29
30
30
31
32
33
33
34
34
35
35
36
37
37
38
38
39
39
39
40
40
42
42
4
Controllers
43
Representational State Transfer . . . . .
Routing . . . . . . . . . . . . . . . . . . . . .
Resources . . . . . . . . . . . . .
Custom Routes . . . . . . . . . .
The Root Route . . . . . . . . . .
Paths and URLs . . . . . . . . .
Controller Actions . . . . . . . . . . . . . .
A Brief Detour from Actions .
Back to Controller Actions . .
Adding Comments . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
Views
57
Embedded Ruby . . . . . . . . . . . . .
Output . . . . . . . . . . . . .
Control Flow . . . . . . . . .
Comments . . . . . . . . . . .
Helpers . . . . . . . . . . . . . . . . . . .
URL Helpers . . . . . . . . . .
Number Helpers . . . . . .
Your Own Helpers . . . . .
The Posts Index Page . . . . . . . . . .
Layouts . . . . . . . . . . . . . . . . . . .
Asset Tag Helpers . . . . .
CSRF Meta Tags Helper .
Yield . . . . . . . . . . . . . .
Partials . . . . . . . . . . . . . . . . . . .
Collections . . . . . . . . . .
Showing Comments . . . .
Forms . . . . . . . . . . . . . . . . . . . .
Form Helpers . . . . . . . . .
Form Errors . . . . . . . . . .
Form Controls . . . . . . . .
Comment Form . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
6
Deployment
Version Control . . . . . . . .
Git . . . . . . . . . . . . . . . .
Setup . . . . . . . .
Getting Started .
Basic Usage . . .
43
45
45
46
47
47
48
49
52
54
55
56
58
58
58
59
59
59
60
61
61
64
65
66
67
67
67
68
69
70
70
71
72
74
74
75
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
75
76
76
76
77
Contents in Detail
ix
Heroku . . . . . . . . . . . . . . . . . . . . . .
Getting Started . . . . . . . . . .
Updating Your Gemfile . . . . .
Deploying Your Application .
Summary . . . . . . . . . . . . . . . . . . . . .
Part I Remarks . . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
81
81
81
83
84
84
85
Part II
Building a Social Networking App
7
Advanced Ruby
Modules . . . . . . . . . . . . . . . . . . . .
Modules as Namespaces .
Modules as Mixins . . . . . .
Ruby Object Model . . . . . . . . . . . .
Ancestors . . . . . . . . . . . .
Methods . . . . . . . . . . . . .
Class . . . . . . . . . . . . . . .
Introspection . . . . . . . . . . . . . . . . .
Duck Typing . . . . . . . . . . . . . . . . .
Metaprogramming . . . . . . . . . . . .
define_method . . . . . . . . .
class_eval . . . . . . . . . . . .
method_missing . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . . . .
89
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
8
Advanced Active Record
Advanced Data Modeling . . . . . . . .
Self Join Associations . . . . .
Many-to-Many Associations .
Single-Table Inheritance . . .
Polymorphic Associations . .
The Social Application . . . . . . . . . . .
User Model . . . . . . . . . . . .
Post Models . . . . . . . . . . . .
Comment Model . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . . . . .
x
Contents in Detail
. 90
. 91
. 91
. 95
. 95
. 96
. 96
. 97
. 98
. 99
. 99
100
102
104
104
105
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
106
106
107
110
111
112
112
117
120
120
121
9
Authentication 123
The Authentication System . . . .
Post Index and Show .
Sign Up . . . . . . . . . .
Log In . . . . . . . . . . . .
Current User . . . . . . .
Authenticate User . . . .
Use Current User . . . .
Summary . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
10
Testing
141
Testing in Rails . . . . . . . . . . . . . . . . . . . . . . . .
Preparing to Test . . . . . . . . . . . . . . . .
Running Tests . . . . . . . . . . . . . . . . . . .
Using Fixtures . . . . . . . . . . . . . . . . . .
Putting Assertions to Work . . . . . . . . . .
Eliminating Duplication with Callbacks .
Model Tests . . . . . . . . . . . . . . . . . . . . . . . . . .
Controller Tests . . . . . . . . . . . . . . . . . . . . . . . .
Controller Test Helpers . . . . . . . . . . . .
Controller Test Assertions . . . . . . . . . .
Integration Tests . . . . . . . . . . . . . . . . . . . . . . .
Integration Helpers . . . . . . . . . . . . . . .
Testing a Flow . . . . . . . . . . . . . . . . . .
Adding Features with Test-Driven Development . .
Show User . . . . . . . . . . . . . . . . . . . .
Create Post . . . . . . . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
11
Security
Authorization Attacks . . . . . . . . . . .
Injection Attacks . . . . . . . . . . . . . .
SQL Injection . . . . . . . . . .
Cross-Site Scripting . . . . .
Cross-Site Request Forgery Attacks .
How CSRF Works . . . . . .
Preventing CSRF . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . . . .
125
125
128
132
135
136
137
138
139
142
142
142
144
146
147
148
150
150
150
152
152
152
154
154
157
161
161
163
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
163
165
166
167
170
170
170
172
172
Contents in Detail
xi
12
Performance
175
Built-in Optimization Features . . . . . . .
Asset Pipeline . . . . . . . . . . .
Turbolinks . . . . . . . . . . . . . .
Code Optimizations . . . . . . . . . . . . .
Reducing Database Queries .
Pagination . . . . . . . . . . . . . .
Caching . . . . . . . . . . . . . . . . . . . . . .
Cache Keys . . . . . . . . . . . . .
Low-Level Caching . . . . . . . .
Fragment Caching . . . . . . . .
Issues . . . . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13
Debugging
The debug Helper . . . . . . . . . . .
The Rails Logger . . . . . . . . . . . .
Log Levels . . . . . . . . . .
Logging . . . . . . . . . . .
Debugger . . . . . . . . . . . . . . . . .
Entering the Debugger .
Debugger Commands . .
Summary . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . .
195
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
14
Web APIs
The GitHub API . . . . . . . . . . . . . . . .
HTTP . . . . . . . . . . . . . . . . . . . . . . .
Status Codes . . . . . . . . . . .
Curl . . . . . . . . . . . . . . . . .
Authentication . . . . . . . . . . . . . . . .
Your Own API . . . . . . . . . . . . . . . .
API Routes . . . . . . . . . . . . .
API Controllers . . . . . . . . . .
Customizing JSON Output .
Token-Based Authentication .
Summary . . . . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . . . . .
xii
Contents in Detail
176
176
179
180
180
183
185
187
187
189
191
192
193
196
197
197
198
199
200
200
204
204
205
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
206
207
207
208
209
211
211
212
213
216
222
222
15
Custom Deployment
Virtual Private Servers . . . . . . .
Amazon AWS Setup .
Ubuntu Linux Setup . . .
Capistrano . . . . . . . . . . . . . . .
Getting Started . . . . .
Configuration . . . . . .
Database Setup . . . . .
Secrets Setup . . . . . . .
Add to Git . . . . . . . . .
Deployment . . . . . . . .
Adding a Virtual Host .
Summary . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . .
Solutions
223
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
224
224
225
230
231
232
233
234
234
235
236
237
237
239
Index 259
Contents in Detail
xiii
F o r e w o rd
Ruby on Rails turned web development upside down.
By abstracting the core of web programming in an
unparalleled way, this unique piece of technology
changed the game forever. With Rails, you can write
web applications quickly without compromising quality. You can be very productive, write little code, deal
with almost no configuration, and adapt to changes
in specifications with agility, all while keeping a wellorganized and elegant code base.
With Ruby on Rails, you feel empowered. Want to explore something
with a quick prototype? Delivered in no time. Need to develop a solid
production-ready website? Presto!
A decade later, the fundamental principles underlying the Rails breakthrough still permeate and drive the design of the framework and the way
Rails applications are developed. You’ll learn about these fundamental
aspects of the Rails culture explicitly in the second chapter of Rails Crash
Course and implicitly by example throughout the book.
While the foundational ideas behind Ruby on Rails remain key, the
framework has evolved. Ruby on Rails has been extended here, shrunk
there, iterated, and refined. The world in which Rails applications live has
also evolved. Rails Crash Course presents the most modern and idiomatic
Ruby on Rails.
But first things first. Ruby on Rails is a web framework written in the
Ruby programming language. Think about Rails as a huge Ruby library: A
Rails application is written in Ruby and uses the classes and core support
provided by Ruby on Rails. Therefore, you definitely have to know some
Ruby in order to write Ruby on Rails applications! The first chapter of Rails
Crash Course introduces Ruby in case you are not familiar with it. Ruby is a
powerful programming language, but it is easy to learn, and with that quick
introduction, you’ll know enough to begin. Later, more advanced Ruby is
explained.
Once you know some Ruby, you’re going to learn Rails. All aspects of
the framework are covered, including how to write models that get persisted
easily in a database, how to validate data, how to handle web requests, how
to serve HTML, and so on.
Rails Crash Course covers all sides of Ruby on Rails, but then it takes you
beyond the basics. For example, you’ll learn some advanced Active Record,
authentication, and how to write an application that provides a REST API,
but you’ll also learn about testing, security, performance, debugging, and
other practical concerns of writing real-world web applications.
Further, Rails Crash Course guides you step-by-step all the way through
uploading your application to production platforms and seeing it run on
the Internet. That’s an amazing experience. You’ll learn how to deploy to
Heroku and how to deploy to a computer in the Amazon cloud. While the
servers needed to run an application for learning are small and free, they are
the real thing: You’ll upload to the exact same services big companies are
deploying their applications to. Rails Crash Course is a superb introduction
to Ruby on Rails, and by reading it, you’ll get a solid understanding of Ruby
on Rails and its ecosystem in a broad sense.
Welcome, and enjoy!
Xavier Noria
Cubelles, Spain
July 2014
xvi Foreword
Ac k n o w l e dg m e n t s
First, I’d like to thank everyone at No Starch Press for giving me the opportunity to write this book. Jennifer Griffith-Delgado guided me through the
process of writing this book. As a first-time author, I found her corrections
and suggestions invaluable. Serena Yang managed the production of the
book and kept everything moving forward even as I dragged my feet at
times.
Xavier Noria did an outstanding job as the technical reviewer. Not only
did he make sure the code samples were correct and followed best practices,
he also pointed out several places where my explanations could be better.
If there are any mistakes left in this book, I’m sure it’s only because I didn’t
correct them when he pointed them out.
I am grateful to Tim Taylor for introducing me to programming by
teaching me BASIC on a Commodore 64 in the 7th grade. We’ve come a
long way since then. I’d also like to thank a few more of my friends from
the Paris Independent School District who convinced me that I could teach
even though I’m not really a teacher: Karol Ackley, Paula Alsup, Denise
Kornegay, Dee Martin, and Frances Reed.
Thanks to everyone in the amazing Austin technology community.
Special thanks to Austin on Rails and its founder Damon Clinkscales.
Thank you to everyone who attended one of my Rails classes or conference sessions. This book grew out of the curriculum I developed for
those classes. Your questions and comments helped clarify the material
in this book.
Finally, my most heartfelt thanks to my family: my wife, Paige, and
our sons, Matthew and Wyatt. The book is finally done; let’s go play!
xviii Acknowledgments
In t r o d u c t i o n
The Ruby on Rails framework emphasizes developer
productivity, making it possible to implement sites
that would once have taken months to build in a matter of weeks—or even days! Thanks to the Ruby programming language and principles such as convention
over configuration and don’t repeat yourself, Rails developers spend less time configuring their applications and
more time writing code.
Ruby on Rails is also a full-stack web framework, meaning it handles
everything from accessing data in a database to rendering web pages in
the browser. As a full-stack framework, Rails is made up of a seemingly endless list of different components, such as Active Record, the asset pipeline,
CoffeeScript, Sass, jQuery, turbolinks, and a variety of testing frameworks.
This book aims to cut through that list and explain exactly what you
need to know to develop your own Ruby on Rails applications. After you
gain some experience with the fundamentals of Rails, I’ll introduce and
explain new components of the framework as needed.
By the end, you’ll know how to build your own Rails application
from scratch. You’ll add tests to ensure features work as expected, protect
your application and your users from security vulnerabilities, optimize your
application’s performance, and finally deploy your application to your own
server.
Who This Book Is For
I assume you have some experience with web development before starting
this book. You should be familiar with HTML and CSS. You should know
what an H1 element is and how to add images and links to a web page. Some
knowledge of object-oriented programming is helpful but not required.
You’ll use your computer’s terminal (or command prompt) to enter
commands, but you don’t need much prior experience with terminal commands to follow the examples. In addition to the terminal, you’ll also need
a text editor for writing Ruby code. Many Rails developers use a vintage editor, such as Vim or Emacs.
If you don’t already have a preferred text editor, I recommend Sublime
Text. A free trial of Sublime Text is available online at http://www.sublimetext
.com/. The free trial version never expires, but it does occasionally prompt
you to purchase a license.
Overview
This book is divided into two parts. The first part covers the fundamentals
of the Ruby language and the Ruby on Rails framework. The second covers
advanced topics in both Ruby and Ruby on Rails. There are exercises at the
end of every chapter, and solutions for them appear at the end of the book.
Chapter 1: Ruby Fundamentals covers the basics of Ruby, including
datatypes, control flow, methods, and classes.
Chapter 2: Rails Fundamentals covers the basics of Ruby on Rails.
Topics include Rails principles, the directory structure used by Rails applications, and common Rails commands. You’ll create your first Rails application at the end of this chapter!
Chapter 3: Models, Chapter 4: Controllers, and Chapter 5: Views
describe the three parts of the model-view-controller architecture used
by Rails.
Chapter 6: Deployment covers creating a Git repository to store your
application and deploying your application to the web using Heroku.
Once you understand the fundamentals of Ruby and Ruby on Rails,
you’re ready for more advanced topics.
Chapter 7: Advanced Ruby covers Ruby modules, the Ruby object
model, and even a bit of metaprogramming.
Chapter 8: Advanced Active Record covers more advanced Active
Record associations. You’ll also build the data model for a new application
at the end of this chapter.
xx Introduction
Chapter 9: Authentication covers the authentication system used by your
new application. This system allows users sign up for an account, log in to
your application, and log off.
Chapter 10: Testing covers automated testing for each part of your application using the MiniTest framework included with Ruby. This chapter also
discusses test-driven development.
Chapter 11: Security covers common web application security vulnerabilities and explains how to make sure your application is secure.
Chapter 12: Performance covers performance optimizations for Rails
applications. Topics include the optimization features already built in to
Rails, SQL query optimizations, and caching.
Chapter 13: Debugging explains several ways to track down bugs.
Learn how to add to the log files generated by your application and how
to use the interactive debugger for really tough bugs.
Chapter 14: Web APIs explains how to use the GitHub API and then
covers the process of creating your own API for your application.
Finally, Chapter 15: Custom Deployment explains the process of setting up your own server on the Amazon cloud and deploying your application using Capistrano.
Installation
To follow the examples and complete the exercises in this book, you’ll
need the Ruby programming language, the Ruby on Rails framework,
the Git version control system, and the Heroku Toolbelt.
The Ruby language website provides installation instructions at https://
www.ruby-lang.org/en/installation/. Rails is distributed as a collection of Ruby
gems, which you’ll download and install with a single command that depends
on your operating system. (The Ruby on Rails website also provides instructions at http://rubyonrails.org/download/.) You can download Git at http://
git-scm.com/downloads/.
Once you’ve installed Ruby, Rails, and Git, install the latest version
of the Heroku Toolbelt, which you’ll use to deploy your applications to
Heroku. Download the Heroku Toolbelt installer from https://toolbelt.heroku
.com/, and then follow the instructions there to complete the installation.
Ruby, Rails, and Git
The sections below contain detailed installation instructions for Ruby, Rails,
and Git on Mac OS X, Linux, and Windows. If you’re using Mac OS X or
Linux, also see “Multiple Ruby Versions” on page xxiii for an alternative
way to install Ruby. There’s a tool called pik for managing multiple Ruby
versions on Windows, but it hasn’t been updated since 2012, so I won’t
cover it here.
Introduction xxi
Mac OS X
Check your current version of Ruby with ruby --version. If you have Mac OS X
Mavericks, you should already have Ruby version 2.0.0. Otherwise, you need
to install a newer version.
Even if you already have Ruby 2.0.0, I recommend using the Homebrew
package manager on Mac OS X. Homebrew is an easy way to install and
update common development tools on Mac OS X. Instructions for downloading and installing Homebrew are online at http://brew.sh/. Once you
install Homebrew, open a terminal and enter the command brew install
ruby to install the latest version of Ruby.
Next, install Ruby on Rails with the command gem install rails.
Then use Homebrew again to install Git by entering the command brew
install git.
Linux
Installation instructions for Linux differ slightly based on which Linux
distribution you are using. First, check your package manager; it may have
a recent version of Ruby. If so, just install that package as you would any
other.
If not, you’ll need to install Ruby from source. Download the current
stable version from https://www.ruby-lang.org/en/downloads/. Unpack the file
and then enter the following commands in a terminal:
$ ./configure
$ make
$ sudo make install
Once the installation is complete, install Ruby on Rails by entering the
command sudo gem install rails.
Every Linux distribution includes Git. Install Git with your package
manager if it’s not already installed on your system.
Windows
You’ll use RubyInstaller to install Ruby. Download the RubyInstaller and
the matching Development Kit from http://rubyinstaller.org/downloads/.
First, click the latest Ruby version on the RubyInstaller download page
to download the installer; at the time of writing, it’s 2.0.0-p484. Then scroll
down to the section labeled Development Kit and click the link under your
version of Ruby to download the Development Kit. As of this writing, for
Ruby 2.0, you’d choose DevKit-mingw64-32-4.7.2-20130224-1151-sfx.exe. If
you are using a 64-bit version of Windows, then download the 64-bit version of the installer and the matching 64-bit Development Kit, currently
DevKit-mingw64-64-4.7.2-20130224-1151-sfx.exe.
Once these downloads finish, double-click the RubyInstaller file and
then follow the prompts on your screen to install Ruby. Once that is complete, double-click the DevKit file and follow the prompts to install the
Development Kit.
xxii Introduction
Once you’ve installed Ruby and the Development Kit, install Rails by
opening a command prompt and entering gem install rails. This will connect to the RubyGems server. Then download and install the various packages that make up the Ruby on Rails framework.
Finally, download the latest version of Git and double-click the file to
complete the installation.
Multiple Ruby Versions
Several third-party tools exist to make it easier to install and manage multiple versions of Ruby on a single computer. This can be useful if you maintain several different applications or if you want to test an application on a
different version of Ruby.
The Ruby on Rails website recommends managing your Ruby installation with rbenv and the ruby-build plugin. The rbenv command switches
between Ruby versions and ruby-build provides the rbenv install command
that you use to install different versions of Ruby.
Installing rbenv
If you’re using Mac OS X, both rbenv and ruby-build can be installed using
Homebrew. Instructions for installing Homebrew are online at http://brew.sh/.
Open a Terminal, enter brew install rbenv ruby-build, and skip to
“Installing Ruby” on page xxiv.
On Linux, install rbenv and ruby-build by cloning the code from GitHub
as shown below. Complete installation instructions are available online at
https://github.com/sstephenson/rbenv/.
First, make sure you have the proper development tools installed. The
ruby-build wiki at https://github.com/sstephenson/ruby-build/wiki/ contains a
suggested build environment for most popular Linux distributions. For
example, on Ubuntu, enter the following command to install everything
you need to compile Ruby.
$ sudo apt-get install autoconf bison build-essential git \
libssl-dev libyaml-dev libreadline6 \
libreadline6-dev zlib1g zlib1g-dev
Reading package lists... Done
Building dependency tree
--snip-Do you want to continue? [Y/n]
Type the letter y to install these packages, and press enter. Packages
needed for other Linux distributions are listed on the wiki page above.
Next, enter the following command to clone the rbenv git repository
into your home directory.
$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
Cloning into '/home/ubuntu/.rbenv'...
--snip-Checking connectivity... done.
Introduction xxiii
Then, add the ~/.rbenv/bin directory to your $PATH and add a line to
your .bashrc file to initialize rbenv each time you log on.
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
$ source ~/.bashrc
Finally, install ruby-build by cloning its git repository into the rbenv
plugins directory with the following command.
$ git clone https://github.com/sstephenson/ruby-build.git \
~/.rbenv/plugins/ruby-build
Cloning into '/home/ubuntu/.rbenv/plugins/ruby-build'...
--snip-Checking connectivity... done.
Once you have rbenv and ruby-build installed, you’re ready to install Ruby.
Installing Ruby
Enter the command rbenv install -l to list the currently available Ruby
versions.
$ rbenv install -l
Available versions:
1.8.6-p383
1.8.6-p420
1.8.7-p249
1.8.7-p302
--snip--
Ignore the versions with words such as jruby, rbx, and ree at the beginning. For now, just focus on the version numbers. The latest version as
of this writing is 2.1.1. If there is a newer version when you install rbenv,
replace 2.1.1 with the correct version number in the command below.
$ rbenv install 2.1.1
Downloading yaml-0.1.6.tar.gz...
--snip-Installed ruby-2.1.1 to /home/ubuntu/.rbenv/versions/2.1.1
Once this completes, enter rbenv global 2.1.1 to set your system’s global
default Ruby version. Now install Ruby on Rails by entering gem install rails.
Finally, update rbenv by entering rbenv rehash. You can learn more about how
rbenv lets you switch Ruby versions at the rbenv website https://github.com/
sstephenson/rbenv/.
xxiv Introduction
Part I
R u b y o n R a i l s F u nd a m e n t a l s
1
R u b y F u nd a m e n t a l s
In 1993, Yukihiro “Matz” Matsumoto combined parts
of his favorite languages (Perl, Smalltalk, Eiffel, Ada,
and Lisp) to create his own ideal language, which he
called Ruby.
Ruby is a dynamic, object-oriented programming language that also
supports imperative and functional programming styles. It focuses on simplicity, productivity, and developer happiness. The Ruby website refers to it
as “A Programmer’s Best Friend,” and developers with experience in other
languages usually find Ruby easy to write and natural to read.
A solid foundation in Ruby is essential to understanding Ruby on Rails,
so I’ll cover Ruby fundamentals in this chapter. As we progress through the
language features, I’ll demonstrate common idioms used by experienced
Ruby developers, so you can use them in your own programs later.
Interactive Ruby
My favorite way to explore the Ruby language is through the Interactive Ruby
interpreter (IRB). Most of the time, I develop applications in a text editor, but
I still keep an IRB session open to test ideas quickly.
To start IRB, open a terminal (or command prompt on Windows), type
irb, and press enter. You should see a prompt similar to this:
irb(main):001:0>
If you see an error message after entering irb, then you probably don’t
have it installed. Check out the Introduction, and follow the Ruby installation instructions to get IRB set up.
IRB is a type of program called a read-eval-print loop (REPL). IRB reads
your input, evaluates it, and displays the result. It repeats this process until
you press ctrl-D or enter quit or exit.
Try out IRB by typing a few words surrounded by quotation marks:
irb(main):001:0> "Hello, Ruby"
=> "Hello, Ruby"
Ruby evaluates the expression you entered and displays the result. A
simple string evaluates to itself, but this isn’t the same as printing the string.
To output something on the screen, use the Ruby method puts, as shown here:
irb(main):002:0> puts "Hello, Ruby"
Hello, Ruby
=> nil
Now Ruby outputs the string to the screen and displays nil, which is
the result of evaluating the puts method. In Ruby, every method returns
something. The puts method doesn’t have anything useful to return, so it
returns nil.
As you work through the rest of this chapter, you’ll find more examples
that you can enter into IRB. I encourage you to try them out and explore
what you can do with IRB and Ruby.
Note
If IRB stops evaluating what you’re typing, you may have “confused” it by forgetting
a closing quotation mark or some other syntax it was expecting. If this happens, press
ctrl-C to cancel the current operation and return to a working prompt.
Now, let’s take a look at the data types available in Ruby.
4 Chapter 1
Data Types
Ruby has six main data types: number, string, symbol, array, hash, and
Boolean. In this section, I’ll briefly discuss each of these data types and
how to use them.
Numbers
Ruby supports the math operations you learned in school, plus a few you
may not have seen before. Type an expression into IRB and press enter to
see the result:
irb(main):003:0> 1 + 1
=> 2
We asked Ruby to evaluate the expression 1 + 1, and it responded with
the result, which is 2. Try out a few more math operations. Everything
should work as expected, at least until you try division, as shown here:
irb(main):004:0> 7 / 3
=> 2
Ruby performs integer division by default. In other words, it drops the
remainder. You can find that remainder with the modulus operator (%). If
you’d rather get a fractional answer, however, you need to tell Ruby explicitly to use floating-point math by including a decimal point and zero after at
least one of the numbers. Here, you can see examples of both the modulus
operator and floating-point division in IRB:
irb(main):005:0> 7 % 3
=> 1
irb(main):006:0> 7.0 / 3
=> 2.3333333333333335
This concept is important to understand: although these appear to be
simple math operators, they are actually methods in Ruby. You can even call
methods on data types that other languages consider primitives.
irb(main):007:0> 1.odd?
=> true
Here, we ask the number 1 if it is odd and IRB responds with true.
Ruby Fundamentals 5
Strings
You can create strings by surrounding characters with single or double
quotes, as in this example:
irb(main):008:0> 'A String!'
=> "A String!"
You can also combine strings in Ruby to create larger ones. The language
understands both adding strings and multiplying a string by a number.
Let’s look at an example of each:
irb(main):009:0> "Hello" + "World"
=> "HelloWorld"
irb(main):010:0> "Hi" * 3
=> "HiHiHi"
Notice that Ruby doesn’t automatically put spaces between words when
adding or multiplying. You are responsible for that detail.
Until now, I haven’t differentiated between single- and double-quoted
strings, but double-quoted strings actually allow you to combine strings in
more complex ways. For example, they support a feature called string interpolation, in which Ruby evaluates an expression surrounded by #{ and },
converts the result to a string, and inserts it into the string automatically,
as shown here:
irb(main):011:0> x = 10
=> 10
irb(main):012:0> "x is #{x}"
=> "x is 10"
In this case, #{x} evaluates to 10, so Ruby converts the number 10 to a
string and returns "x is 10".
Double-quoted strings also support special characters such as newlines
and tabs. These special characters consist of a backslash followed by a letter.
Type \n to create a newline (shown next) or \t to create a tab. To add a literal backslash in a double-quoted string, type two backslashes.
irb(main):013:0> puts "Line one\nLine two"
Line one
Line two
=> nil
You’ve already seen a few string methods, but many others are handy,
including length and empty?. (Yes, methods in Ruby can end with question
marks and even exclamation marks.) Let’s look at those two methods in
action:
irb(main):014:0> "Hello".length
=> 5
6 Chapter 1
irb(main):015:0> "Hello".empty?
=> false
The length method returns the number of characters in a string,
whereas empty? tells you whether a string contains any characters.
Note
A question mark at the end of method name, as in empty?, indicates that it is a predicate, and it will return a true or false value. An exclamation mark (!) usually signifies that the method does something dangerous such as modifying the object in place.
Symbols
Ruby has a data type not often seen in other programming languages, and
that’s the symbol. Symbols are similar to strings in that they are made of
characters, but instead of being surrounded by quotes, symbols are prefixed
with a colon, like this:
irb(main):016:0> :name
=> :name
Symbols are typically used as identifiers. They are created only once
and are unique. This means they are easy for programmers to read as a
string, but also memory efficient. You can see this for yourself by creating
a few strings and symbols and then calling the object_id method on them.
irb(main):017:0> "name".object_id
=> 70156617860420
irb(main):018:0> "name".object_id
=> 70156617844900
irb(main):019:0> :name.object_id
=> 67368
irb(main):020:0> :name.object_id
=> 67368
Notice that the two strings here have the same content, but different
object ids. These are two different objects. The two symbols have the same
content and the same object id.
When Ruby compares two strings for equality, it checks each individual
character. Comparing two symbols for equality requires only a numeric
comparison, which is much more efficient.
Arrays
An array represents a list of objects in Ruby. You create an array by surrounding a list of objects with square brackets. For example, let’s make an
array of numbers:
irb(main):021:0> list = [1, 2, 3]
=> [1, 2, 3]
Ruby Fundamentals 7
Ruby arrays can contain any kind of object, even other arrays. You can
access individual elements of an array by passing a numeric index to the
array’s [] method. The first element is at index zero. Try examining the first
element in the array just created:
irb(main):022:0> list[0]
=> 1
Entering list[0] tells Ruby to fetch the first number in the array, and
the method returns 1.
Note
If you try to access an element that isn’t in the array, the [] method will return nil.
You can also pass two numbers to the [] method to create an array slice,
as shown next. The first number you provide specifies the starting index,
whereas the second tells it how many elements you want in your array slice:
irb(main):023:0> list[0, 2]
=> [1, 2]
Here, the [] method starts at index zero and returns the first two numbers in list.
Like strings, you can also add arrays to create a new one using the
+ operator. If you just want to add elements to the end of an existing array,
you can use the << operator. You can see an example of each operation here:
irb(main):024:0> list + [4, 5, 6]
=> [1, 2, 3, 4, 5, 6]
irb(main):025:0> list << 4
=> [1, 2, 3, 4]
Though the + operator returns a new array, it doesn’t modify the existing array. The << operator does modify the existing array. You can also use
an index to reassign an existing element or add a new element to the array.
Hashes
A hash is a set of key-value pairs. In Ruby, hashes are enclosed in curly
braces. Unlike an array index, a hash key can be of any data type. For
example, symbols are frequently used as hash keys. When you need to
access a value in a hash, just pass the corresponding key to the [] method,
as shown next. Attempting to access a key that does not exist returns nil.
irb(main):026:0> some_guy = { :name => "Tony", :age => 21 }
=> {:name=>"Tony", :age=>21}
irb(main):027:0> some_guy[:name]
=> "Tony"
The combination of an equal sign and a greater-than sign (=>) between
the key and value is commonly referred to as a hash rocket. Because symbols
8 Chapter 1
are used as hash keys so often, Ruby 1.9 added a shorthand syntax specifically for them. You can take the colon from the front of the symbol, put it at
the end, and then leave out the hash rocket. Here’s an example:
irb(main):028:0> another_guy = { name: "Ben", age: 20 }
=> {:name=>"Ben", :age=>20}
Although you can create a hash with this shorthand, Ruby seems to be
sentimental as it still uses the old syntax when displaying the hash.
You can also use the keys method to get an array of all keys in a hash.
If you need an array of all the values in the hash, use the method values
instead. The code here shows an example of each method, using the same
hash just created:
irb(main):029:0> another_guy.keys
=> [:name, :age]
irb(main):030:0> another_guy.values
=> ["Ben", 20]
Hashes are frequently used to represent data structures, as in these
examples. They are also sometimes used to pass named parameters to a
method. If a hash is the last (or only) argument to a method call, you can
even leave off the curly braces.
For example, the merge method combines two hashes. The code here
merges the hash named another_guy with a new hash containing { job: "none" }.
irb(main):031:0> another_guy.merge job: "none"
=> {:name=>"Ben", :age=>20, :job=>"none"}
Because the only argument to this method call is the new hash, you
can leave off the curly braces. Rails has many other examples of this type
of method call.
Booleans
A Boolean expression is anything that evaluates to true or false. These
expressions often involve a Boolean operator, and Ruby supports familiar
operators including less than (<), greater than (>), equal (==), and not equal
(!=). Try these Boolean expressions at the IRB prompt:
irb(main):032:0> 1 < 2
=> true
irb(main):033:0> 5 == 6
=> false
Ruby also provides and (&&) and or (||) operators for combining multiple Boolean expressions, as shown next:
irb(main):034:0> 1 < 2 || 1 > 2
=> true
Ruby Fundamentals 9
irb(main):035:0> 5 != 6 && 5 == 5
=> true
Both of these operators short circuit. That is, && is only true if the expressions on both sides evaluate to true. If the first expression is false, then the
second expression is not evaluated. Likewise, || is true if either expression
is true. If the first expression is true, then the second expression is not
evaluated.
The || operator is also sometimes used with assignment. You might do
this when you want to initialize a variable only if it is currently nil and keep
the current value otherwise. Ruby provides the ||= operator for this case.
This is referred to as conditional assignment, and you can see an example here:
irb(main):036:0> x = nil
=> nil
irb(main):037:0> x ||= 6
=> 6
If the variable x had not been a false value, then the conditional assignment would have returned the value of x instead of setting it to 6.
Note
Any expression in Ruby can be evaluated as a Boolean expression. In Ruby, only
nil and false are considered false. Every other value is considered true. This differs
from some other languages, where things like empty strings, empty collections, and the
number zero are considered false.
Constants
A constant gives a name to a value that doesn’t change. In Ruby, the name of
a constant must begin with a capital letter. Constants are typically written in
uppercase, like this one:
irb(main):038:0> PI = 3.14
=> 3.14
irb(main):039:0> 2 * PI
=> 6.28
Ruby won’t actually stop you from assigning a new value to a constant,
but it does display a warning if you do.
Variables
In Ruby, you don’t need to declare a variable in advance or specify a type.
Just assign a value to a name as shown here:
irb(main):040:0> x = 10
=> 10
10 Chapter 1
The variable x now refers to the number 10. Variable names are typically
written in snake case, that is, all lowercase with underscores between words.
irb(main):041:0> first_name = "Matthew"
=> "Matthew"
Variable names can include letters, numbers, and underscores, but they
must start with either a letter or underscore.
Control Flow
The examples we’ve looked at so far have all been linear. Real programs usually include statements that only execute when a certain condition is met
and statements that are repeated multiple times. In this section, I cover
Ruby’s conditional statements and iteration.
Conditionals
Conditional statements let your program choose between one or more branches
of code to execute based on an expression you provide. As such, making a
decision in code is also called branching. For example, the following conditional prints the word Child only if the expression age < 13 evaluates to true.
irb(main):042:0> age = 21
=> 21
irb(main):043:0> if age < 13
irb(main):044:1>
puts "Child"
irb(main):045:1> end
=> nil
The variable age is set to 21, so age < 13 will evaluate to false, and nothing will be printed.
You can also use elsif and else to make more complicated conditionals.
Let’s look at a code example that has to check multiple conditions:
irb(main):046:0>
irb(main):047:1>
irb(main):048:1>
irb(main):049:1>
irb(main):050:1>
irb(main):051:1>
irb(main):052:1>
Adult
=> nil
if age < 13
puts "Child"
elsif age < 18
puts "Teen"
else
puts "Adult"
end
This code can take three different branches depending on the value of
age. In our case, it should skip the code inside the if and elsif statements
and just print Adult.
Ruby Fundamentals 11
All of the previous conditional examples checked for true expressions,
but what if you want to execute a block of code when an expression is false
instead? Like other languages, Ruby has a logical not operator (either not or
!), which is useful here. The following example will print the value of name if
it is not an empty string.
irb(main):053:0> name = "Tony"
=> "Tony"
irb(main):054:0> if !name.empty?
irb(main):055:1>
puts name
irb(main):056:1> end
=> nil
When name.empty? is false, the ! operator should reverse the result to
true so the code inside the if statement executes. A more natural way to
say this conditional might be “unless name is empty, print its value.” Unlike
an if statement, Ruby’s unless statement executes code when the expression
evaluates to false.
irb(main):057:0> name = "Tony"
=> ""
irb(main):058:0> unless name.empty?
irb(main):059:1>
puts name
irb(main):060:1> end
=> nil
That still seems a little wordy to me. For one-line expressions such as
this, Ruby lets you put the conditional at the end of the line:
irb(main):061:0> name = "Tony"
=> ""
irb(main):062:0> puts name unless name.empty?
=> nil
This example is concise and readable. To me, this code says “print name
unless it’s empty.” This code is also a great example of Ruby’s flexibility. You
can write conditional expressions using the style that makes the most sense
to you.
Iteration
When you’re working with a collection of objects, such as an array or hash,
you’ll frequently want to perform operations on each item. In addition to
the for loops seen in other languages, Ruby collections provide the each
method.
12 Chapter 1
The each method accepts a block of code and executes it for every element in the collection. A block in Ruby usually starts with the word do and
ends with the word end. A block can also accept one or more parameters,
which are listed inside a pair of pipe characters. The each method returns
the value of the entire collection.
This next example iterates over each element in the array list, which
we created earlier in this chapter as [1, 2, 3, 4]. It assigns the element to the
variable number and then prints the value of number.
irb(main):063:0> list.each do |number|
irb(main):064:1>
puts number
irb(main):065:1> end
1
2
3
4
=> [1, 2, 3, 4]
Simple blocks like this are often written on one line in Ruby. Instead of
writing do and end to indicate a block, you can use opening and closing curly
braces, which are common in one-line blocks. Like the previous example,
this one iterates over the list and prints each element, but it does everything
in a single line of code.
irb(main):066:0> list.each { |n| puts n }
1
2
3
4
=> [1, 2, 3, 4]
You can also use the each method to iterate over a hash. Because a hash
is a collection of key-value pairs, the block will take two parameters. Let’s
try using each with one of our earlier hashes:
irb(main):067:0> some_guy.each do |key, value|
irb(main):068:1>
puts "The #{key} is #{value}."
irb(main):069:1> end
The name is Tony.
The age is 21.
=> {:name=>"Tony", :age=>21}
Blocks are useful for more than just iteration. Any method can potentially accept a block and use the code it contains. For example, you can pass
a block to the File.open method. Ruby should pass the file handle as a variable to the block, execute the code within the block, and then close the file
automatically.
Ruby Fundamentals 13
Methods
A method is a named block of reusable code. Defining your own methods in
Ruby is simple. A method definition starts with the word def, followed by a
name, and continues until end. This method will print “Hello, World!” each
time it is called:
irb(main):070:0> def hello
irb(main):071:1>
puts "Hello, World!"
irb(main):072:1> end
=> nil
As you can see in the example, a method definition should return nil.
Note
If you’re using Ruby 2.1, method definitions return the name of the method as a
symbol.
Once you’ve defined a method, you can call it by entering its name at
the IRB prompt:
irb(main):073:0> hello
Hello, World!
=> nil
Ruby methods always return the value of their last statement; in this
case, the last statement was puts, which returns nil. You can use return to
return a value explicitly, or you can just add the value you wish to return
as the last line of the method.
For example, if you want the hello method to return true, you can modify it like this:
irb(main):074:0> def hello
irb(main):075:1>
puts "Hello, World!"
irb(main):076:1>
true
irb(main):077:1> end
=> nil
Now call the method as before:
irb(main):078:0> hello
Hello, World!
=> true
Because the last line of the method is the value true, the method returns
true when called.
In Ruby, you specify method parameters by adding them after the
method name, optionally enclosed in parentheses, as shown in the next
example. Parameters can also have default values.
14 Chapter 1
irb(main):079:0> def hello(name = "World")
irb(main):080:1>
puts "Hello, #{name}!"
irb(main):081:1> end
=> nil
This example redefines the hello method to accept a parameter called
name. This parameter has a default value of "World". This method can be
called as before to display “Hello, World!”, or you can pass a value for the
name parameter to greet someone else.
irb(main):082:0> hello
Hello, World!
=> nil
irb(main):083:0> hello "Tony"
Hello, Tony!
=> nil
The parentheses around method arguments are also optional. Include
them if the intention is not clear; otherwise, feel free to omit them.
Classes
In an object-oriented programming language such as Ruby, a class represents the state and behavior of a distinct type of object. In Ruby, an object’s
state is stored in instance variables, and methods define its behavior. A
Ruby class definition starts with the word class, followed by a capitalized
name, and continues to the matching end.
Class definitions can include a special method called initialize. This
method is called when a new instance of the class is created. It is typically
used to assign values to the instance variables needed by the class. In Ruby,
instance variables start with an @, as shown in the following class definition:
irb(main):084:0> class Person
irb(main):085:1>
def initialize(name)
irb(main):086:2>
@name = name
irb(main):087:2>
end
irb(main):088:1>
def greet
irb(main):089:2>
puts "Hi, I'm #{@name}."
irb(main):090:2>
end
irb(main):091:1> end
=> nil
This code defines a new class called Person. The initialize method takes
one parameter and assigns the value of that parameter to the instance variable @name. The greet method prints a friendly greeting. Let’s write some
code that uses this new class.
Ruby Fundamentals 15
irb(main):092:0> person = Person.new("Tony")
=> #
irb(main):093:0> person.greet
Hi, I'm Tony.
=> nil
You can create an instance of the Person class by calling Person.new and
passing the required parameters. The previous example creates an instance
of Person with the name Tony.
The return value of Person.new is a string representation of the object. It
consists of the class name followed by a reference to the object in memory
and a list of instance variables. Calling the greet method should display the
friendly greeting we expect.
Instance variables, like @name, are not accessible outside of the class. Try
to access person.name from the IRB prompt, and you should see an error.
irb(main):094:0> person.name
NoMethodError: undefined method 'name'
If you need to access or change @name outside of the class, you need to
write a getter and a setter. These are methods that get or set the value of an
instance variable. Fortunately, Ruby classes provide the method attr_accessor,
which writes getters and setters for you.
You would normally include attr_accessor :name in your definition of the
Person class. Rather than retype the entire class definition, you can reopen
the class and add this line:
irb(main):095:0> class Person
irb(main):096:1>
attr_accessor :name
irb(main):097:1> end
=> nil
This code adds the attr_accessor call to the Person class and updates all
objects of the class automatically. And this is another example of the Ruby’s
flexibility. You can reopen a class, even at runtime, and add new methods as
needed.
Now, if we want to change the name of our person, we can just set it
equal to something else, as shown here:
irb(main):098:0> person.name
=> "Tony"
irb(main):099:0> person.name = "Wyatt"
=> "Wyatt"
irb(main):100:0> person.greet
Hi, I'm Wyatt.
=> nil
16 Chapter 1
The attr_accessor method uses the symbol :name to define the getter name
and the setter name=. You can now get and set the value of the instance variable as needed. If you only want a getter, include a call to attr_reader instead
of attr_accessor. Doing this lets you read the value of @name, but not change it.
Class Methods
The attr_accessor method is different from the methods I’ve discussed so
far. Note that attr_accessor is called inside the body of the class definition.
The methods you’ve seen so far, such as the greet method, are called on an
instance of a class.
In Ruby, methods called on an instance of a class are called instance
methods. Methods called on the class itself are called class methods. Another
example of a class method is new. When you typed Person.new("Tony") before,
you were calling the class method new of the class Person.
Inheritance
In Ruby, you can define a new class that builds on the state and behavior of
an existing class, and the new class will inherit variables and methods from
the existing one. Inheritance defines an is-a relationship between those two
classes. For example, a student is a person. We can define the class Student
like this:
irb(main):101:0> class Student < Person
irb(main):102:1> def study
irb(main):103:2>
puts "ZzzzZzzz"
irb(main):104:2> end
irb(main):105:1> end
=> nil
The < Person on the first line indicates that the Student class inherits
from the Person class. The variables and methods defined by Person are now
available to Student:
irb(main):106:0> student = Student.new("Matt")
#
u irb(main):107:0> student.greet
Hi, I'm Matt.
=> nil
irb(main):108:0> student.study
ZzzzzZzzzz
=> nil
Because we created greet on Person earlier in the chapter, we can have
any Student call this method u without defining it in our new class.
Ruby only supports single inheritance, which means that one class
can’t inherit from multiple classes at the same time. You can, however,
work around this limitation by using modules. A module is a collection of
Ruby Fundamentals 17
methods and constants that cannot be instantiated but can be included in
other classes to provide additional behavior. We discuss modules and other
advanced features of Ruby in Chapter 7.
Summary
You are now well on your way to becoming a great Ruby on Rails programmer. The Ruby knowledge you gained in this chapter will make understanding the Rails framework much easier.
I recommend working with IRB as much as you need to feel comfortable with Ruby. When you’re ready to start exploring Rails, enter exit to
leave IRB, and continue on to Chapter 2.
Exercises
1.
You can read plaintext files in Ruby with the File.read method. Create
a file containing a paragraph or two from a blog post or book, and name
it test.txt in the current directory. This next code sample reads a file
named test.txt into the variable file and displays the contents of the file:
file = File.read("test.txt")
puts file
2.
3.
As you can see, file contains a string. Use file.split to convert
the string into an array of words. You can now use Ruby’s built-in
array methods to operate on the contents of the file. For example, use
file.split.length to count words in the file. file.split.uniq.length tells
you how many unique words are in the file.
Using the array of words from Exercise 1, count how many times each
word appears in the file. One way to do this is by iterating over the
array and storing the count for each word in a hash where the key is
the word and the value is the count.
Create a WordCounter class to perform the operations from Exercises 1
and 2. The class should accept a filename to read when it is initialized and include methods named count, uniq_count, and frequency for
performing the operations from the previous two exercises. The following class definition should help you get started:
class WordCounter
def initialize(file_name)
@file = File.read(file_name)
end
# your code here...
end
18 Chapter 1
2
R a i l s F u nd a m e n t a l s
Ruby on Rails is an open source web framework. Like
the Ruby language, it emphasizes programmer happiness and productivity. As you’ll see, it includes sensible
defaults that allow you to spend less time dealing with
configuration and more time writing code. It also creates a directory structure for your application with a
place for every file you need.
Rails was created by David Heinemeier Hansson. He extracted the
framework from the project management application Basecamp that he
built for 37signals. It was first released as open source in July 2004.
Rails is also a full-stack web framework. This means it contains everything you need to build web applications that accept user requests, query
databases, and respond with data rendered in templates.
Enter the following command in a terminal to ensure that Rails is
installed:
$ rails --version
This should display Rails 4.0.0 or greater. If not, check the installation
instructions for Rails in “Ruby, Rails, and Git” on page xxi.
Your First Rails Application
Rails makes getting started easy, so let’s dive right in. You’re only five commands away from having a running Rails web application.
Just like Jedi Knights build their own lightsabers, I think web developers should build their own personal websites, so that’s where we’ll start. I
use my website as a playground for testing new ideas. In the fast-moving
world of Ruby on Rails, keeping your own site up-to-date also helps you
learn to use new features as they are released.
Open a terminal window and create a directory for your Rails projects.
I call mine code, but you can use any name you like.
$ mkdir code
$ cd code
Now use the rails new command to create a new application. Our first
application will be a simple weblog, so let’s just call it blog.
$ rails new blog
This command creates all of the files your new application needs and
then runs the bundle install command to download and install any other
gems needed by Rails. (Gems are packaged Ruby applications or libraries.)
Depending on your connection speed, this may take a few minutes. When
the install is complete, use the cd command to move to the newly created
blog directory:
$ cd blog
Finally, use the rails server command to launch a server so you can see
your new application. When you created this application, a directory called
bin was also created inside blog. The bin directory is where you’ll find rails
and other commands you’ll need later.
$ bin/rails server
This command starts the WEBrick server that is built in to Ruby. Once
the server has started, open your web browser and go to this address: http://
localhost:3000. If everything worked correctly, you should see a web page like
the one in Figure 2-1.
20 Chapter 2
Don’t Repeat Yourself
The other key principle in Rails is don’t repeat yourself, often abbreviated
DRY. In Rails, you avoid duplicating knowledge within your application.
Specifying the same information in more than one place can lead to errors
when you change one instance and not the other.
You’ll see several examples of the DRY principle as we work our way
through the Rails architecture and directory structure. There is a single,
specific place for each part of a Rails application. Things that can be inferred
from another source, such as the names of columns in a database table,
don’t need to be specified anywhere.
Rails Architecture
Rails applications are structured around the model-view-controller (MVC)
software engineering pattern. The MVC pattern is designed to separate
an application’s data from a user’s interaction with it. This separation of
concerns usually results in an application that is easier to understand and
maintain.
Model
The model represents your application’s data and the rules for manipulating
that data. The application’s data is sometimes referred to as the application’s
state. The rules for manipulating this data are also known as business logic. All
changes to your application’s state must pass through the model layer.
Rails models contain code for data validation and associations between
models. Most of the code you write will be inside of a Rails model, unless it
is directly involved with the user’s view of the data.
View
The view is the user interface for your application. Because we are building
web applications, the view will consist mainly of HTML. Rails uses a template system called Embedded Ruby (ERB) by default.
Embedded Ruby allows you to include Ruby code for accessing data
within an HTML template. When the user requests a page, the Ruby code
in the template is evaluated by the server, and the resulting HTML page is
sent to the user.
The ability to embed Ruby code inside a view can sometimes lead programmers to include too much code inside a view. Doing this is problematic
because if you add another view, then that code will need to be duplicated.
Code used only by the view can be moved to a helper, which is a method
meant specifically for use in the view. In general, a view should never contain code more complex than a simple conditional statement.
Note
22 Chapter 2
In addition to HTML pages, Rails can also generate JSON and XML. Ruby has
built-in support for generating CSV files, and gems are available for generating other
types of output, such as PDF documents and Excel spreadsheets.
Controller
The controller is like the glue that holds together the model and the view. The
controller is responsible for accepting a request from the user, gathering
the necessary data from the model, and rendering the correct view. This
sounds like a lot of work, but thanks to the conventions used in Rails applications, the process happens almost automatically.
In Rails, the controller is simply a Ruby class with methods that correspond to the various actions in your application. For example, in your blog
application, you have a method named show for displaying a blog post and a
method named new for adding a new post.
Rails Application Structure
Now that you’re familiar with the principles and architecture used
by Rails, let’s see where these pieces live within the directory structure
created by the rails new command. Inside the blog directory, you should
find 10 subdirectories.
The app Directory
The app directory is where you’ll spend most of your time while building
your application. It contains subdirectories for each part of the MVC architecture discussed previously, as well as assets, helpers, and mailers.
The assets directory holds the images, JavaScript files, and CSS style
sheets used by your application. The helpers directory contains Ruby files
with methods used by your views. The mailers directory is for Ruby classes
used to send email.
The bin Directory
The bin directory holds simple Ruby scripts for accessing the bundle, rails,
and rake command-line programs used while building your application.
These scripts ensure that the three programs run in the context of the current Rails application. You can have multiple versions of these programs
installed at the same time, which can lead to errors if you don’t use the
scripts in bin to access them.
The config Directory
Rails makes heavy use of convention over configuration, but sometimes configuration is unavoidable. In those cases, look to the config directory.
The environments subdirectory contains configuration files for the three
different environments created automatically by Rails. An environment is a
collection of settings used for a specific purpose such as development or testing. These settings are stored in development.rb (used while developing your
application), test.rb (used while running automated tests), and production.rb
(used after your application is deployed and running in production).
Rails Fundamentals 23
The file application.rb contains the settings for all environments.
Information in one of the specific environment files just mentioned will,
however, take precedence over settings here.
The file database.yml holds database configuration for each of the three
environments. Rails creates a SQLite database, by default, when you run
rails new, so the default settings in database.yml are for that SQLite database.
This database is a single file that you will use during development. You usually want to specify a different database server to use in production.
The file routes.rb maps the web address entered by the user to a specific
controller and action in your application. As you add resources and actions
to your application, you need to update this file to reflect the changes. I cover
resource-based routing during the discussion of controllers in Chapter 4.
The db Directory
The db directory initially contains only a single file called seeds.rb. Use this
file to create your application’s default data. For example, in an application
with user accounts, you may want to include a special “admin” user here.
As you build your application, you will create database migrations, Ruby
scripts that create and modify the tables in your database. A directory
named migrate is created to hold these database migration files. The file
schema.rb, which shows the current state of your application’s database, is
created as well. If you use the default SQLite database in your application,
the database itself is also placed in this folder.
The lib Directory
The lib directory is the place to put any reusable library code you write.
This directory is initially empty except for two subdirectories: assets and
tasks. Assets are images, CSS stylesheets, and JavaScript files. Tasks are Ruby
scripts used to automate actions such as managing your application’s database, clearing log and temporary files, and running tests. These tasks are
executed using the rake command.
The log Directory
As your application runs, data is written to a file in the log directory. When
you run your code in the development environment, this file is named
development.log. Separate files will be created for the test and production
environments.
The public Directory
Files in the public directory are sent to users as if the files were in the root
directory of your application. For example, three files in this directory are
for error messages—404.html, 422.html, and 500.html. You can see one of
these files in your browser by adding its name to your address bar. If you
visit http://localhost:3000/404.html, for example, you should see the default
“page does not exist” error page.
24 Chapter 2
This directory also holds a default favicon.ico, the image that appears
in the address bar of most browsers, and a robots.txt file that controls how
search engines index your application. You can modify all of these files for
your application. You’ll probably want to customize the error pages with your
own branding and add a custom favicon for your site.
The test Directory
The test directory contains subdirectories with automated tests for each part
of your application. It also holds the script test_helper.rb, which loads the test
environment settings in config/environments/test.rb and adds helper methods
used in your tests.
Some Rails developers practice test-driven development (TDD). In TDD,
you first write an automated test describing a new feature; then you add just
enough code to make the test pass; and finally you refactor, or restructure,
the code as needed to improve readability and reduce complexity.
The tmp Directory
The tmp directory contains ephemeral files. Here, you find cached copies of
your application’s assets, process id files (pids) for running programs (such
as your web server), user sessions, and files representing sockets being used
by your application.
Note
Because these files usually do not need to be saved, version control systems ignore them.
The vendor Directory
Finally, the vendor directory holds assets needed by third-party gems that
you add to your application. Its purpose is similar to the lib directory,
except it is used by libraries that you did not write yourself.
Rails Commands
You use four different command-line programs when building Rails applications. These can sometimes be confusing to new Rails developers.
The gem Command
The gem command installs Ruby gems. The Rails framework is actually
distributed as a collection of gem files.
Your newly created Rails application is made up of over 40 gems.
Maint aining the correct versions of these gems and dependencies between
them can get complicated. For this reason, you rarely use the gem command
directly; instead, you usually rely on a tool called Bundler to manage gems
and keep your dependencies up to date. You interact with Bundler using
the bundle command.
Rails Fundamentals 25
The bundle Command
The bundle command is used to install and update the gems needed by your
application. It installs gems by reading the Gemfile that was automatically
created by the rails new command in the root directory of your Rails application. It stores the version numbers of gems you’re using and their dependencies in the file Gemfile.lock.
The bundle list command displays the names and versions of all gems
currently being used by your application:
$ bin/bundle list
As mentioned earlier, we are using the copy of bundle inside the bin
directory.
The rake Command
The rake command is an automated build tool used to run tasks related to
your application. (If you are familiar with the make command, rake is the
Ruby version.) Enter this command to obtain a list of the available tasks
for your application:
$ bin/rake --tasks
This command prints a list of tasks your application can use along with
a short description of each to your terminal. Some gems add tasks to your
application, and you can also add your own tasks by writing Ruby scripts
and saving them in the lib/tasks directory.
The rails Command
You used the rails command earlier to create an application and start the
server. You can also use this command to generate new code and launch the
console. Enter the rails command by itself to list the available options:
$ bin/rails
In addition to the new command that you used to build your application
and the server command that starts a web server, Rails also provides several
other helpful commands. These include the generate command (generates
new code), the console command (starts an interactive Ruby console with
your Rails application preloaded), and the dbconsole command (starts a
command-line interface for the currently configured database).
Now that you’ve seen some Rails command-line tools, let’s use a few of
them to add some functionality to the blog application we created.
26 Chapter 2
Rails Scaffold
We’re going to use a Rails feature known as scaffolding. Rails scaffolding
is a sometimes-controversial feature that generates application code for
you. This single command creates a model, a set of views, and a controller
automatically.
Many developers believe that you should write all of your own code. I
agree with them, but Rails scaffolding is helpful for bootstrapping an application, especially for developers new to Ruby on Rails. We will explore the
generated code over the next few chapters. By the end, you will understand
each file and be able to write those files by hand.
Open a new terminal window (or a new tab in your current one).
Change to your code/blog directory if necessary. Then, use the rails generate
command to add posts to the blog:
$ bin/rails generate scaffold Post utitle:string vbody:text
Here, we’ve asked Rails to generate a scaffold for a blog Post. We specified that a Post should have a title u and a body v.
The title will be a string, and the body will be a text field in the database.
As this command runs, you should see a flurry of activity in your terminal
as files are generated and placed in the correct folders.
The previous command should have generated a database migration.
Use the rake command to run that migration:
$ bin/rake db:migrate
This command should create a table named posts with fields named
id, title, body, created_at, and updated_at. The title and body fields will store
data entered by the user. Rails adds the id, created_at, and updated_at fields
automatically. The id field is a unique, auto-incrementing integer that represents each row in the database. The created_at and updated_at fields are
timestamps representing when the row was created and when it was last
updated. Rails keeps track of these values automatically.
To see the results, go to http://localhost:3000/posts in your web browser.
You should see a page like the one in Figure 2-2.
Figure 2-2: The Rails Post scaffolding
Rails Fundamentals 27
This page certainly won’t win any awards for design, but it is functional.
Click the New Post link to see a form for adding a new blog post. After you
add a post, click the Back link to return to the home page.
By default, Rails shows your data in a table with links for Show, Edit, and
Destroy. Feel free to try these links and verify that the application is working.
As you play with the application, be sure to look at the output in the
terminal window where the server is running. This is a copy of the development log generated by your application. You’ll find a wealth of information
here, such as the URL requested, the Ruby method being run, and the SQL
commands being executed.
Summary
This chapter covered the basic principles, architecture, directory structure,
and commands used to build Rails applications. In the next chapter, we’ll
dig in to the Rails code we just generated, starting with models, and you’ll
learn to write your own code.
Exercises
1.
2.
28 Chapter 2
Explore the functionality of your new blog. Create, edit, and destroy
posts. View the list of posts and individual posts. Note how the URL
in the address bar changes as you move around the application.
Get used to moving around the various files in the blog application in
your editor of choice. If you’re using Sublime Text 2, you can open the
blog directory itself and then use the sidebar to open individual files.
3
Models
In Rails, models represent the data in your application and the rules to manipulate that data. Models
manage interactions between your application and a
corresponding database table. The bulk of your application’s business logic should also be in the models.
This chapter covers Active Record, the Rails component that provides
model persistence (that is, storing data in the database), as well as data validations, database migrations, and model associations. Validations are rules
to ensure that only valid data is stored in the database. You create database
migrations to change the schema of the database, and associations are relationships between multiple models in your application.
The Post Model
In the previous chapter, we used the Rails scaffold generator to build a
simple blog with models, views, and controllers for blog posts. Look at the
post model created by the scaffold generator by opening the file app/models/
post.rb in your favorite text editor.
class Post < ActiveRecord::Base
end
There’s not much to see here. Right now, the file just tells us that the
class Post inherits from ActiveRecord::Base. Before I talk about what you can
actually do with Post, let’s begin our discussion with Active Record.
Active Record
Active Record is an implementation of the object-relational mapping (ORM)
pattern described, using the same name, by Martin Fowler in Patterns of
Enterprise Application Architecture (Addison-Wesley Professional, 2002). It’s
an automated mapping between classes and tables as well as attributes and
columns.
Each table in your database is represented by a class in your application. Each row of that table is represented by an instance (or object) of the
associated class, and each column of that row is represented by an attribute
of that object. The example in Table 3-1 demonstrates this structure. If you
could look inside your database, this is what you would see.
Table 3-1: The Posts Table
id
title
body
created_at
updated_at
1
Hello, World
Welcome to my blog...
...
...
2
My Cat
The cutest kitty in the...
...
...
3
Too Busy
Sorry I haven’t posted...
...
...
Table 3-1 holds three example blog posts. This table is represented by
the Post class. The post with an id of 1 can be represented by a Post object.
Let’s call our object post.
You can access the data associated with a single column by calling an
attribute method on the object. For example, to see the post’s title, call
post.title. The ability to access and change database values by calling attribute methods on an object is known as direct manipulation.
Create, Read, Update, and Delete
Let’s explore Active Record further by entering a few commands in the Rails
console. The Rails console is the IRB that you used in Chapter 1 with your
Rails application’s environment preloaded.
To start the Rails console, go to your blog directory and enter bin/rails
console. You might notice that the console takes a little longer to start than
the IRB. During that slight pause, your application’s environment is being
loaded.
As with the IRB, you can enter exit to quit the console when you’re done.
30 Chapter 3
The four major functions of database applications are create, read,
update, and delete, usually abbreviated as CRUD. Once you know how to perform these four actions, you can build any type of application you need.
Rails makes these actions easy for you. In most cases, you can accomplish each with a single line of code. Let’s use them now to work with posts
on our blog.
Create
We’ll start by adding a few records to the database. Enter these commands in
the Rails console as you work through this section. The remaining e xamples
in this chapter use these records.
The easiest way to create a record in Rails is with the appropriately
named create method, as shown here:
u
2.1.0 :001 > Post.create title: "First Post"
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "posts" ("created_at"...
(1.9ms) commit transaction
=> #
The Rails console displays the SQL being sent to the database as commands are run u. In the interest of brevity, I’m going to omit these SQL
statements in the rest of the samples.
The create method accepts a hash of attribute-value pairs and inserts
a record into the database with the appropriate values. In this case, it’s
setting the title attribute to the value "First Post". When you run this
example, the values for id, created_at, and updated_at are set for you automatically. The id column is an auto-incrementing value in the database,
whereas created_at and updated_at are timestamps set for you by Rails. The
body column is set to NULL since no value was passed for it.
The create method is a shortcut for instantiating a new Post object,
assigning values, and saving it to the database. If you don’t want to take the
shortcut, you could also write a separate line of code for each action:
2.1.0 :002 > post = Post.new
=> #
2.1.0 :003 > post.title = "Second Post"
=> "Second Post"
2.1.0 :004 > post.save
=> true
We had to use multiple commands this time, but just like before, we’ve
created a brand new Post object. Two posts are now stored in the database.
In both examples, we only assigned values to the post’s title attribute, but
you would assign values to the post body in exactly the same way. Rails
assigns values to id, created_at, and updated_at automatically. You shouldn’t
change these.
Models 31
Read
Once you have a few posts in your database, you’ll probably want to read
them back out for display. First, let’s look at all of the posts in the database
with the all method:
2.1.0 :005 > posts = Post.all
=> #, #]>
This returns an Active Record relation, which contains an array of all
posts in your database, and stores it in posts. You can chain additional methods onto this relation, and Active Record combines them into a single query.
Active Record also implements the first and last methods, which return
the first and last entries in an array. The Active Record version of these
methods returns only the first or last record in the database table. This is
much more efficient than fetching all of the records in the table and then
calling first or last on the array. Let’s try fetching a couple of posts from
our database:
2.1.0 :006
=> # # Post.first
id: 1, title: "First Post", ...>
> Post.last
id: 2, title: "Second Post", ...>
This example returns the first and last posts, as ordered by id. You’ll
learn how to order records by a different field in the next section. Sometimes,
however, you’ll know exactly which record you want, and it might not be the
first or last one. In that case, you can use the find method to retrieve a record
by id.
2.1.0 :008 > post = Post.find 2
=> #
Just don’t ask find to fetch a record that doesn’t exist. If a record
with the specified id isn’t in your database, Active Record will raise an
ActiveRecord::RecordNotFound exception. When you know a specific record
exists but you don’t know its id, you can use the where method to specify an
attribute that you do know:
2.1.0 :009 > post = Post.where(title: "First Post").first
=> #
The where method also returns a relation. If more than one record
matches, you can chain the all method after where and tell Rails to retrieve
all matching records on demand when they are needed.
32 Chapter 3
If you know the database has only one matching record, you can chain
the first method after where to retrieve that specific record as in the previous example. This pattern is so common that Active Record also provides
the find_by method as a shortcut:
2.1.0 :010 > post = Post.find_by title: "First Post"
=> #
This method takes a hash of attribute-value pairs and returns the first
matching record.
Update
Updating a record is as easy as reading it into a variable, changing values
via direct manipulation, and then saving it back to the database:
2.1.0 :011 > post = Post.find 2
=> #
2.1.0 :012 > post.title = "2nd Post"
=> "2nd Post"
2.1.0 :013 > post.save
=> true
Rails also provides the update method, which takes a hash of attributevalue pairs, updates the record, and saves to the database all on one line:
2.1.0 :014 > post = Post.find 2
=> #
2.1.0 :015 > post.update title: "Second Post"
=> true
The update method, like the save method, returns true when successful
or false if it has a problem saving the record.
Delete
Once you have read a record from the database, you can delete it with the
destroy method. But this time don’t type in these commands. You don’t want
to delete the posts you created earlier!
2.1.0 :016
=> # # post = Post.find 2
id: 2, title: "Second Post", ...>
> post.destroy
id: 2, title: "Second Post", ...>
The destroy method can also be called on the class to delete a record by
id, which has the same effect as reading the record into a variable first:
2.1.0 :018 > Post.destroy 2
=> #
Models 33
You can also delete records based on a relation:
2.1.0 :019 > Post.where(title: "First Post").destroy_all
=> [#]
This example deletes all records with a title of "First Post". Be careful
with the destroy_all method, however. If you call it without a where clause,
you’ll delete all records of the specified class!
More Active Record Methods
If you’re familiar with SQL or other methods of accessing records in a database, you know there’s much more to working with a database than simple
CRUD. Active Record provides methods for more database operations, such
as ordering, limiting, counting, and other calculations.
Query Conditions
In addition to the simple where conditions you’ve seen so far, Active Record
also has several methods to help refine your queries. The order method
specifies the order of returned records; limit specifies how many records
to return; and offset specifies the first record to return from a list.
The limit and offset methods are often used together for pagination.
For example, if you want to show 10 blog posts per page, you can read the
posts for the first page like this:
2.1.0 :020 > posts = Post.limit(10)
=> #, #]>
To read the posts for the second page of your site, you’ll need to skip
the first 10 posts:
2.1.0 :021 > posts = Post.limit(10).offset(10)
=> #
Entering this returns an empty set since we only have two posts in
our database. When you combine offset with limit in this way, you can
pass offset multiples of what you passed limit to view different pages of
your blog.
You can also change how the entries in a relation are ordered. When
using limit, the order of records returned is undefined, so you need to
specify an order. With the order method, you can specify a different order
for the set of records returned:
2.1.0 :022 > posts = Post.limit(10).order "created_at DESC"
=> #, #]>
Using DESC tells order to return the posts from newest to oldest. You
could also use ASC to order them the opposite way. If you would rather see
34 Chapter 3
posts alphabetized by title, try replacing "created_at DESC" with "title ASC".
The order method defaults to ascending order if you don’t specify ASC or
DESC, but I always give an order so my intention is clear.
Calculations
Databases also provide methods for performing calculations on records.
We could read the records and perform these operations in Ruby, but the
methods built in to the database are usually optimized to be faster and use
less memory.
The count method returns the number of records matching a given
condition:
2.1.0 :023 > count = Post.count
=> 2
If you don’t specify a condition, count counts all records by default, as in
this example.
The sum, average, minimum, and maximum methods perform the requested
function on a field. For example, this line of code finds and returns the
date on the newest blog post:
2.1.0 :024 > date = Post.maximum :created_at
=> 2014-03-12 04:10:08 UTC
The maximum created_at date you see should match the date for your
newest blog post, not necessarily the date you see in the example.
Migrations
Database migrations are used any time you need to change your database’s
structure. When we used the scaffold generator to create blog posts, it generated a migration for us, but you can also create migrations yourself. As
you build your application, your database migrations contain a complete
record of the changes made to your database.
Migration files are stored in the db/migrate directory and start with a
timestamp that indicates when they were created. For example, you can
see the migration created by the scaffold generator by editing the file
db/migrate/*_create_posts.rb. (Because the timestamps on your files will
surely be different from mine, I’ll use an asterisk from now on to refer to
the date part of the filename.) Let’s look at that file now:
u
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.text :body
t.timestamps
Models 35
end
end
end
Database migrations are actually Ruby classes. The change method is
called u when the migration is run. In this case, the method creates a table
named posts with fields for title, body, and timestamps. The timestamps field
refers to both the created_at and updated_at fields. Rails also automatically
adds the id column.
You can run migrations as tasks with the rake command. For example,
you enter bin/rake db:migrate to run all pending migrations and bring your
database up-to-date.
Rails keeps track of which migrations have been run by storing the
timestamps in a database table called schema_migrations.
If you make a mistake in a database migration, use the db:rollback task
to undo it. After you correct the migration, use db:migrate to run it again.
The Schema
In addition to the individual migration files, Rails also stores your database’s current state. You can see this by opening the file db/schema.rb.
Ignoring the comment block at the top of the file, it should look like this:
--snip-ActiveRecord::Schema.define(version: 20130523013959) do
create_table
t.string
t.text
t.datetime
t.datetime
end
"posts", force: true do |t|
"title"
"body"
"created_at"
"updated_at"
end
This file is updated whenever you run a database migration. You should
not edit it manually. If you are moving your application to a new computer
and would like to create a new, empty database all at once instead of by
r unning the individual migrations, you can do that with the db:schema:load
rake task:
$ bin/rake db:schema:load
Running this command resets the database structure and removes all
of your data in the process.
36 Chapter 3
Adding a Column
Now that you know more about migrations, let’s create one and run it.
When we created our blog post model, we forgot that posts need authors.
Add a string column to the posts table by generating a new migration:
$ bin/rails g migration add_author_to_posts author:string
The Rails generator (g is short for generate) looks at the name of your
migration, in this case, add_author_to_posts, and tries to figure out what
you want to do. This is another example of convention over configuration:
name your migration in the format add_ColumnName_to_TableName, and Rails
will parse that to add what you need. Based on the name, we clearly want to
add a column named author to the posts table. We also specified that author
is a string, so Rails has all the information it needs to create the migration.
Note
You can name a migration anything you want, but you should follow the convention
so you don’t have to edit the migration manually.
Enter bin/rake db:migrate to run the migration and add the author column to your database. If you still have a Rails console open, you’ll need to
exit and restart with bin/rails console for your changes to take effect. You
can also look at the db/schema.rb file to see the new column in the posts table.
Inside the Author Migration
The code you just generated for adding a column is simple. Edit the file
db/migrate/*_add_author_to_posts.rb to see how it works.
class AddAuthorToPosts < ActiveRecord::Migration
def change
add_column :posts, :author, :string
end
end
Like *_create_posts.rb, this migration is a class containing a change method.
The add_column method is called with the table name, column name, and column type. If you want to add multiple columns, you could create separate
migrations for each, or you could call this method multiple times.
Active Record migrations also provide the rename_column method for
changing a column’s name, the remove_column method for removing a column from a table, and the change_column method for changing a column’s
type or other options, such as default value.
Models 37
Validations
Remember that models have rules for manipulating application data. Active
Record validations are sets of rules created to protect your data. Add validation rules to ensure that only good data makes it into your database.
Adding a Validation
Let’s look at an example. Because we’re making a blog, we should ensure
that all posts have a title so readers don’t get confused, and we can do that
with a validation rule.
Validations are implemented as class methods in Rails. Open the post
model (app/models/post.rb) in your editor and add this line:
class Post < ActiveRecord::Base
validates :title, :presence => true
end
This validates the presence of text in the title field. Attempting to
create a blog post with a blank title should now result in an error.
Othe r Common Va lidat ions
Rails provides a variety of other validations in addition to the :presence validation. For example, you can use the :uniqueness validation to ensure that no two
posts have the same title.
The :length validation accepts a hash of options to confirm that the value
is the correct length. Adding this line to your post model confirms that all titles
are at least five characters:
validates :title, :length => { :minimum => 5 }
You can also specify a :maximum value instead of a :minimum, or you can
use :is to set an exact value.
The :exclusion validation ensures the value does not belong to a given set
of values. For example, adding this validation prohibits blog posts with the title
Title:
validates :title, :exclusion => { :in => [ "Title" ] }
You can think of :exclusion as a blacklist for values you don’t want to allow.
Rails also provides an :inclusion validation for specifying a whitelist of accepted
values.
38 Chapter 3
Testing Data
Validations are automatically run before data is saved to the database.
Attempt to store invalid data, and save returns false. You can also test a
model manually with the valid? method:
2.1.0 :025 > post = Post.new
=> #
2.1.0 :026 > post.valid?
=> false
2.1.0 :027 > post.errors.full_messages
=> ["Title can't be blank"]
In this example, the valid? method should return false because you
didn’t set a value for the title. Failing validations add messages to an array
called errors, and calling full_messages on the errors array should return a
list of error messages generated by Active Record based on your validations.
Use validations freely to keep bad data out of your database, but also
consider your users when you create those validations. Make it clear which
values are valid, and display error messages if invalid data is given so the
user can correct the mistake.
Associations
Only the simplest of applications contain a single model. As your application grows, you’ll need additional models, and as you add more, you’ll
need to describe the relationships between them. Active Record associations
describe the relationships between models. For example, let’s add comments
to our blog posts.
Posts and comments are associated. Each post has many comments, and
each comment belongs to a post. This one-to-many relationship is one of the
most commonly used associations, and we’ll explore it here.
Generating the Model
A blog comment should have an author, a body, and a reference to a post.
You can easily generate a model using that information:
$ bin/rails g model Comment author:string body:text post:references
Note
Remember to run database migrations after generating this new model!
The post:references option tells the Rails generator to add a foreign key
to the comments database table. In this case, the foreign key is named post_id
because it refers to a post. The post_id field contains the id of this comment’s
post. The migration created the column we need in the database, so now we
need to edit our models to finish setting up the association.
Models 39
Adding Associations
First, open app/model/post.rb again to add the comments association. Earlier
I said that each post has many comments, and that’s the association we
need here:
class Post < ActiveRecord::Base
validates :title, :presence => true
has_many :comments
end
Rails uses a class method called has_many to create this association in
a readable way. Now, edit app/model/comment.rb, and you’ll see that the
Rails generator already added the matching belongs_to statement for you
automatically:
class Comment < ActiveRecord::Base
belongs_to :post
end
The post to comments association should now work as intended. If your
Rails console was still running while you made these changes, you’ll need to
restart it to see the effects.
Using Associations
When you create an association in a model, Rails automatically defines
several methods for that model. Use these methods, and you won’t have to
worry about keeping the post_id updated. They maintain this relationship
for you automatically.
The has_many Methods
The has_many :comments statement you saw inside Post defines several methods:
Returns an Active Record relation representing the array of
comments for this post
comments< Adds an existing comment to this post
comments= Replaces the existing array of comments for this post with
a given array
comment_ids Returns an array of the comment ids associated with
this post
comment_ids= Replaces the existing array of comments for this post
with the comments corresponding to the given array of ids
comments
Because the comments method returns a relation, it is commonly used
with other methods. For example, you can create new comments associated with a post with post.comments.build, which builds a new comment
belonging to this post, or post.comments.create, which creates a new comment belonging to this post and saves it to the database. Each of these
40 Chapter 3
methods automatically assigns the post_id of the newly created comment.
This example creates a new comment associated with your first post. You
should see the new comment in the output from post.comments:
2.1.0 :028 > post = Post.first
=> #
2.1.0 :029 > post.comments.create :author => "Tony", :body => "Test comment"
=> #
2.1.0 :030 > post.comments
=> #]>
If you want to check if any comments are associated with a post, use
comments.empty?, which returns true if there are none. You might also find it
helpful to know how many comments are associated with a particular post;
in that case, you use comments.size:
2.1.0 :031 > post.comments.empty?
=> false
2.1.0 :032 > post.comments.size
=> 1
When you know a post has comments associated with it, you can look
for a particular comment by passing post.comments.find a comment id. This
method raises an ActiveRecord::RecordNotFound exception if a matching
comment cannot be found belonging to this post. Use post.comments.where
instead if you would rather not raise an exception. This method just returns
an empty relation if a matching comment is not found.
The belongs_to Methods
The belongs_to :post statement inside the Comment model defines five methods.
Because belongs_to is a singular association (a comment can only belong to
one post), all of these methods have singular names:
Returns an instance of the post that this comment belongs to
post= Assigns this comment to a different post
build_post Builds a new post for this comment
create_post Creates a new post for this comment and saves it to the
database
create_post! Creates a new post for this comment but raises
ActiveRecord::RecordInvalid if the post is not valid
post
These methods are the inverse of the methods defined in the Post model.
Use them when you have a comment and you would like to manipulate its
post. For example, let’s fetch the post associated with our first comment:
2.1.0 :033 > comment = Comment.first
=> #
2.1.0 :034 > comment.post
=> #
Models 41
Calling post on the first comment, which is also our only comment so
far, should return our first post. This confirms the association works both
ways. Assuming you still have more than one post in your database, you can
also assign this comment to a different post:
2.1.0 :035 > comment.post = Post.last
=> #
2.1.0 :036 > comment.save
=> true
Assigning a comment to another post updates the comment’s post_id,
but does not write that to the database. Don’t forget to call save after updating the post_id! If you make this common mistake, the comment’s post_id
won’t actually change.
Summary
This chapter has been a whirlwind tour of Active Record, so play around
in the console until you’re comfortable with these ideas. Add more posts,
update the existing posts with body text, and create comments associated
with these posts. Focus on the CRUD operations and association methods
in particular. These methods are commonly used in all Rails applications.
The next chapter covers Rails controllers. There, you’ll see all of these
methods in use as you work your way through the various controller actions.
Exercises
1.
2.
3.
42 Chapter 3
It might be nice to contact the people leaving comments on our blog.
Generate a new migration to add a string column to the comments
table to store an email address. Run this migration, and use the Rails
console to verify that you can add an email address to comments now.
We need to ensure that users actually enter some text when they create
a comment. Add validations to the comments model for the author and
body fields.
Write a query to determine the number of comments belonging to each
post. You can’t do this with a single query, but you should be able to find
the answer by iterating over a collection of posts as if it were an array.
4
Controllers
Rails controllers connect your application’s models and
views. Any web requests that your application receives
are routed to the appropriate controller. The controller gets data from the model and then renders the
appropriate view or redirects to a different location.
In this chapter, we continue working on our blog. Along the way, you’ll
learn about controllers in detail. I’ll cover resource representation with
REST, routing resources, and the types of actions a controller can take.
Representational State Transfer
Representational State Transfer, or REST, is a client-server software architecture introduced in 2000 by Dr. Roy Fielding, one of the authors of the
HTTP specification. REST deals with the representation of resources,
and in Rails, resources correspond to models. In RESTful architectures,
clients initiate requests to servers. Servers process those requests and return
Routing
Setting up all of these URLs and mapping actions to verbs might sound
pretty complicated, but luckily, Rails routing handles all of this for you.
Routes connect URLs to the code that comprises an application. First, let’s
look at the most common type of route, the resource route.
Resources
Your application’s routes are stored in the file config/routes.rb. Open that file
in your text editor.
Ignore all of the comments. Your file should only have three lines
right now:
Rails.application.routes.draw do
resources :posts
end
Rails applications use REST by default. The blog application currently
has only one resource (blog posts), and the single line resources :posts
builds a set of routes for your application. Use the rake command to display
your application’s routes:
$ bin/rake routes
Prefix Verb
URI Pattern
posts GET
/posts(.:format)
POST
/posts(.:format)
--snip--
Controller#Action
posts#index
posts#create
This command outputs the route helper prefix, HTTP verb, URL pattern, and controller action for each of the seven default RESTful actions.
For example, a GET request to /posts calls the PostsController#index
method. As you make changes to the routes file, run this command again
to see how your application’s routes also change.
Nested Resources
When one resource belongs to another resource, you can add it as a nested
resource. In the blog, comments belong to posts. Here’s how you represent
that in config/routes.rb:
resources :posts do
resources :comments
end
Add a block after resources :posts with a do, end pair. Then add
resources :comments inside that block. This tells Rails that comments are
only available inside of posts.
Controllers 45
Restricted Resources
Adding resources :comments as you just saw creates routes for each of the
seven default RESTful actions for comments. For now, let’s only worry about
creating new comments. You can restrict the set of routes generated for a
resource by adding an only clause to that resource in config/routes.rb:
resources :posts do
resources :comments, only: :create
end
Now, only the comment create action is mapped to a URL. You should
only provide routes to actions that you plan to implement.
Custom Routes
Some actions in your application may not correspond to any of the seven
default actions. For example, your application may include a search action
that returns a list of posts containing a specific term. In cases such as this,
Rails lets you manually configure custom routes.
Custom routes are also useful for mapping old URLs to a new Rails
application or simplifying URLs for complex actions. For example, imagine
your application allows users to log in by creating a new session and log out
by destroying their session. Adding resources :user_session creates paths like
user_session/new. If you would rather use different paths, you can create custom routes for login and logout.
Rails.application.routes.draw do
resources :posts do
resources :comments, :only => :create
end
get 'login' => 'user_sessions#new'
post 'login' => 'user_session#create'
delete 'logout' => 'user_sessions#destroy'
end
Now your application’s login page should be at the path /login. When
a user visits the login page, his or her browser sends a GET request for
this path. The controller displays the login form in response to that GET
request. When the user submits the form, the browser sends a POST request
to the same path with the contents of the form. The controller then creates
a new session for the user in response to the POST request. When the user
clicks the log out button, a DELETE request to the path /logout destroys the
user’s session.
We aren’t adding authentication to the blog application, but you can
still add these routes to config/routes.rb if you want to see the routes created.
Remove them before moving on because accessing a path that doesn’t correspond to a controller action results in an error.
46 Chapter 4
The Root Route
Finally, let’s create a root route so we don’t have to add /posts to the browser’s
address bar every time. The root route sets the home page for your application. Add root 'posts#index' near the end of config/routes.rb:
Rails.application.routes.draw do
resources :posts do
resources :comments, :only => :create
end
root 'posts#index'
end
Now, accessing your server without a path should display the posts
index page. You should always include a root route for your application.
Paths and URLs
Adding a route also automatically creates helpers for your controllers and
views. You can use these helpers, shown in Table 4-3, instead of manually
typing URLs in your application. That way, if you decide to change your
application’s URLs in the future, you won’t have to search for and update
all of the old URLs in your code.
Table 4-3: Rails Path and URL Helpers
Path Helpers
URL Helpers
posts_path
posts_url
new_post_path
new_post_url
edit_post_path(id)
edit_post_url(id)
post_path(id)
post_url(id)
The path helpers include only the path, whereas the URL helpers also
include the protocol, server, and port (if not standard). Rails applications
generally use path helpers. The URL helpers are useful for situations in which
the full URL is needed, such as to generate URLs for inclusion in emails.
The first part of each method name matches the prefix displayed by the
bin/rake routes command.
You can test these helpers in the Rails console like this:
2.1.0 :001 > app.posts_path
=> "/posts"
2.1.0 :002 > app.post_path(1)
=> "/posts/1"
2.1.0 :003 > app.new_post_path
=> "/posts/new"
2.1.0 :004 > app.root_path
=> "/"
Controllers 47
Testing these helpers is a useful sanity check when working with Rails
routes. If you forget which helper to use to create a path, you can type it in
the console to see the result.
Controller Actions
The convention in Rails is to have a controller corresponding to each
resource. That controller includes methods for each action. (Remember the
principle from Chapter 2: convention over configuration.) The Rails scaffold generator created a controller for posts. Open the file app/controllers/
posts_controller.rb to see the Ruby code behind these methods. I recommend
running the Rails server as you work your way through the rest of this
chapter:
$ bin/rails server
Now, let’s look at each controller method in turn, starting with index
and working our way down to destroy.
The index action retrieves all posts from the database:
def index
@posts = Post.all
end
You see the familiar @post = Post.all in that method. You may be surprised that this is the only line of code in the index method. By default, Rails
renders a view file matching the action name, in this case app/views/posts/
index.html.erb. (We’ll discuss views in the next chapter.)
Go to http://localhost:3000/posts in your browser to see the results of the
index action.
The show action retrieves a single post from the database, but the show
method contains no code at all:
def show
end
This method relies on a Rails before_action, which you should see on
line two of the controller:
before_action :set_post, only: [:show, :edit, :update, :destroy]
The before_action is a class method that automatically calls the set_post
method, shown next, before the methods show, edit, update, and destroy. This
eliminates duplicate code in these methods. (Remember DRY: Don’t repeat
yourself.)
def set_post
@post = Post.find(params[:id])
end
48 Chapter 4
The set_post method is defined near the bottom of the controller under
the keyword private. It calls the Post.find method to retrieve the post with
an id corresponding to a parameter passed to the controller. Parameters
are covered in more detail in the next section, so for now, let’s continue
examining these controller methods.
The new action displays a form for adding a new post:
def new
@post = Post.new
end
The form uses data from a newly created post. Click the New Post link at
the bottom of the post index page to see this form.
The edit action displays a form for editing an existing post. Like the
show method, this method contains no code:
def edit
end
This form uses data retrieved by the set_post method discussed previously.
A Brief Detour from Actions
Before discussing create, update, and destroy, let’s talk about a few key Rails
topics that you need to know to understand those methods. In this section,
we’ll explore parameters, render/redirect, response formats, and the flash.
Parameters
Parameters generally represent part of the URL used to request a page or
values from a form, and they’re accessible in the controller as a hash named
params. For example, the set_post method you saw earlier retrieved the id of
the requested post from the params hash, like this:
@post = Post.find(params[:id])
You can see the parameters passed with each request in the output
from the rails server command in your terminal. For example, go to
http://localhost:3000/posts/1 and then look at the Rails server output in
your terminal:
Started GET "/posts/1" for 127.0.0.1 at 2014-03-31 20:30:03 -0500
Processing by PostsController#show as HTML
u Parameters: {"id"=>"1"}
Post Load (0.3ms) SELECT "posts".* FROM "posts"
WHERE "posts"."id" = ? LIMIT 1 [["id", "1"]]
Rendered posts/show.html.erb within layouts/application (233.9ms)
Completed 200 OK in 274ms (Views: 245.5ms | ActiveRecord: 26.2ms)
Controllers 49
In this case, the 1 in the URL represents the id of the requested post u.
Because we requested a single post, the show method is called, and this id is
used to find the post in set_post.
Form data is represented by a nested hash with values. For example,
editing this post results in a params hash more like this:
{
"utf8"=>"",
"authenticity_token"=>"...",
"post"=>{"title"=>"First Post", "body"=>""},
"commit"=>"Update Post",
"id"=>"1"
u
v
}
You still access params[:id] v to find the correct post, and you can also
access params[:post] u to see the new values submitted by the user. Because
these are user-submitted values, you should ensure that your application
only accepts data for the appropriate attributes. Malicious users could send
requests with invalid parameters in an attempt to attack your application.
For blog posts, you only want users to be able to edit the title and body
attributes. Rails includes a feature called Strong Parameters, which makes
specifying which attributes your application accepts easy. You can see the
feature in action in the post_params method:
def post_params
params.require(:post).permit(:title, :body)
end
This method first requires the params hash to contain a nested hash with
the key :post. It then returns only the permitted values (:title and :body)
from this nested hash. Using the earlier example params hash, post_params
returns a hash like this:
{"title" => "First Post", "body" => ""}
Other values in the params[:post] hash are silently ignored. Remember,
always use the post_params method when accessing the parameters for a
newly created or updated post.
Render or Redirect
Every action must either render a view or redirect to another action. By
default, an action renders a file matching the action name. For example,
the show method in the posts controller looks for a file named app/views/
posts/show.html.erb and uses that file to build the HTML response that is
sent back to the user.
50 Chapter 4
You can tell Rails to render the response for a different action with the
render method like this:
render action: "edit"
The ability to specify actions is helpful if you need to render a different
view based on user input. This example is from the update method. If the
post could not be updated with the data provided by the user, this method
renders the edit view again, giving the user a chance to correct the data.
Sometimes you need to send the user to a page other than the one
he or she requested. Use the redirect_to method to take care of this. For
example, if the user enters valid data while creating or updating a post, the
controller action redirects the user to that post:
redirect_to @post
When you call redirect_to, the address in the user’s browser changes
to reflect the new page, and another request is made. You can see this by
watching the address bar as you submit form data and by looking at the output from rails server in your terminal.
To see this in action, first go to http://localhost:3000/posts/new in your
browser. This is the new post form. Enter a title for the new post, and then
click the Create Post button. Watch the address bar closely after clicking the
button.
The form makes a POST request to http://localhost:3000/posts. This
request is routed to the create method. After creating the post, you are
redirected to http://localhost:3000/posts/3, assuming that your new post has
an id of 3. The address is changed automatically by the redirect_to method.
Response Formats
Rails can generate responses in several formats, though all I’ve discussed
so far is HTML. Scaffold-generated controllers can also include JavaScript
Object Notation (JSON) responses, which are useful for creating application
programming interfaces (APIs). Other formats include XML and even PDF.
You can try another response type in your web browser by visiting
this URL: http://localhost:3000/posts.json. This URL is the same as the
posts index URL used earlier, except it has .json added to the end. Rails
recognizes this as a JSON request and renders the collection of posts as
JSON, as in Figure 4-1.
Figure 4-1: Posts in JSON format
Controllers 51
You specify the formats an action accepts and the responses to each format with a call to the respond_to method. This method accepts a block with
a single parameter representing the requested format. Here is an example
from the destroy method:
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
end
This method is called right after a post is destroyed. If the client
requests HTML data, this block redirects to posts_url, the index page. If
the client requests JSON data, by adding .json to the end of the URL, this
block responds with an empty header to indicate the post no longer exists.
The Flash
Flash messages are alerts to the user that are only valid for a single request.
Flash messages are stored in the user’s session, typically in a cookie. They
are usually styled differently to stand out. For example, the stylesheet
included with Rails scaffolding uses green text for flash messages.
Flash messages are helpful for sending error messages or other notifications to the user. They are generally set on a redirect. Here’s an example
from the create method in the posts controller:
redirect_to @post, notice: 'Post was successfully created.'
When a post is successfully created, the user is redirected to the new
post and a flash message like the one in Figure 4-2 is shown.
Figure 4-2: A flash message
The create flash message is the green text, and it matches the message
added earlier.
Back to Controller Actions
Now you should know everything you need to understand the create, update,
and destroy actions. The methods written by the scaffold generator respond
to requests for both HTML and JSON data with messages indicating success
or errors, but let’s focus on the HTML responses for now. I’ll cover JSON
responses in depth when I talk about building your own APIs.
52 Chapter 4
N OTE
The formatting in each method has been adjusted slightly to better fit this page.
The create method is responsible for creating a post using the params
from the new post form:
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to @post,
notice: 'Post was successfully created.' }
format.json { render action: 'show',
status: :created, location: @post }
else
format.html { render action: 'new' }
format.json { render json: @post.errors,
status: :unprocessable_entity }
end
end
end
The first line of the method @post = Post.new(post_params) uses Strong
Parameters to ensure only the accepted parameters are allowed into the call
to new. Inside the respond_to block, the return value of @post.save is checked.
If it’s true, then the user is redirected to the newly created post. If it is false,
then the new action is rendered again so the user can correct any errors.
The update method is similar to the create method. The main difference
is that the code checks the return value of @post.update instead of @post.save.
def update
respond_to do |format|
if @post.update(post_params)
format.html { redirect_to @post,
notice: 'Post was successfully updated.' }
format.json { render action: 'show',
status: :ok, location: @post }
else
format.html { render action: 'edit' }
format.json { render json: @post.errors,
status: :unprocessable_entity }
end
end
end
If @post.update returns true, the code redirects the user to the updated
post; otherwise, it renders the edit form so the user can correct the errors.
The destroy method is simpler than the create and update methods
because it doesn’t check the return value of @post.destroy.
Controllers 53
def destroy
@post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
end
end
After the post is destroyed, the code redirects the user back to the
index page, posts_url.
Adding Comments
You added a route to the create comment action earlier, so now let’s add a
simple controller for that action. You’ll add the form for entering new comments in the next chapter.
Generate a new controller for comments using the Rails generator:
$ bin/rails generate controller comments
create app/controllers/comments_controller.rb
invoke erb
v
create
app/views/comments
invoke test_unit
create
test/controllers/comments_controller_test.rb
invoke helper
create
app/helpers/comments_helper.rb
invoke
test_unit
create
test/helpers/comments_helper_test.rb
invoke assets
invoke
coffee
create
app/assets/javascripts/comments.js.coffee
invoke
scss
create
app/assets/stylesheets/comments.css.scss
u
Note that I specified only a controller, not scaffolding. This code generates an empty controller u and an empty views directory v, as well as files
for helpers, tests, and assets. We’ll have to fill in the details ourselves. Start
by opening the file app/controllers/comments_controller.rb in your editor:
class CommentsController < ApplicationController
end
Because you’re implementing the create action, the first thing you need
is a create method. You can model it after the create method in the posts
controller. Assume that users won’t be adding comments via an API, so it
isn’t necessary to generate JSON responses.
54 Chapter 4
class CommentsController < ApplicationController
def create
u
@post = Post.find(params[:post_id])
v
w
if @post.comments.create(comment_params)
redirect_to @post,
notice: 'Comment was successfully created.'
else
redirect_to @post,
alert: 'Error creating comment.'
end
end
end
This code first finds the correct post u using the post_id in the params
hash. It then uses the comments association to create a new comment v and
redirects back to the post w. Each call to redirect_to sets a flash message to
indicate success or failure.
Because you’re using Strong Parameters in your application, you also
need to add the comment_params method to specify the parameters you want
to accept.
class CommentsController < ApplicationController
--snip-private
def comment_params
params.require(:comment).permit(:author, :body)
end
end
In the case of comments, you only accept an author and a body. Any
other parameters are ignored. In the next chapter, you’ll update the post
show view to display existing comments and include a form for creating new
comments.
Summary
This chapter introduced many important Rails concepts—REST, routing,
and controllers. I also discussed parameters, render versus redirect, response
formats, and the flash.
We started at the database in the last chapter and worked our way forward in this chapter. In the next chapter, we’ll get all the way to the user
and cover the last piece of the MVC puzzle: views.
Controllers 55
Exercises
1.
2.
56 Chapter 4
Good error messages are important for any application. If something
goes wrong, your users need to know what the problem is and how to
correct it. Currently, if a comment can’t be created, users see the message “Error creating comment.” Update the CommentsController create
method to also show a list of error messages in the alert.
In Exercise 1 at the end of Chapter 3, you added an email field to the
Comment model. Update the comment_params method in CommentsController
to also accept this field.
5
Vie ws
A view is the user interface to your application.
Typically, views include web pages for displaying
database records and forms for creating and updating those records. Views also sometimes take the
form of responses to API requests.
This chapter covers the most common Rails view template type, called
Embedded Ruby, as well as view-specific helpers and layouts. You’ll also learn
how to avoid duplication in your HTML code with partials and how to generate forms to accept user input.
Enter bin/rails server to start the Rails server now. And keep it running
in a terminal window as you work through the examples in this chapter, so
you can see the changes you make to the application in your web browser
and watch the server output.
Embedded Ruby
Embedded Ruby (ERB), the default template type in Rails, is used to build
view templates. An Embedded Ruby template contains a mixture of Ruby
code and HTML that is similar to ASP, JSP, or PHP.
Templates are stored in a subdirectory of app/views named after the
controller. For example, you’ll find the templates for the posts controller in
app/views/posts. The Rails convention is to name templates after the action
they represent, with the file extension .html.erb. The default template for the
index action is index.html.erb.
Embedded Ruby contains three special tags for executing Ruby code.
These tags are used for output, control flow, and comments. Let’s take a
look at each of these.
Output
The <%= %> tag (also called the output tag) executes the code it contains and
prints the return value on the page. Open the file app/views/posts/show.html.erb
in your editor to see several examples of this tag.
For instance, this tag prints the title of the current post:
<%= @post.title %>
Note that any HTML in the title is escaped by default. That is, any
reserved characters are converted to character references and displayed
on the page instead of being interpreted as HTML. This safeguard prevents malicious users from entering HTML code on your page that could
cause a page to break or even a cross-site scripting attack. Cross-site scripting attacks and other security concerns are covered in Chapter 11.
Control Flow
The <% %> tag executes the code it contains without printing anything on
the page. This tag is useful for control flow statements such as loops or conditionals. Open the file app/views/posts/index.html.erb to see this tag in action.
This example uses the each method to loop over an array of posts:
<% @posts.each do |post| %>
<%= post.title %>
<%= post.body %>
<%= link_to 'Show', post %>
<%= link_to 'Edit', edit_post_path(post) %>
<%= link_to 'Destroy', post, method: :delete,
data: { confirm: 'Are you sure?' } %>
<% end %>
58 Chapter 5
Output tags are used inside the loop to print the values of post.title
and post.body. This example also shows three instances of the link_to helper.
These helpers create links to the show, edit, and destroy actions for each post.
We’ll discuss helpers in the next section.
Comments
Finally, the <%# %> tag is used to enter comments. Comments are usually notes
to yourself or other programmers describing what your code does. Unlike
HTML comments (which start with ), ERB comments
will not appear in the HTML source. Use ERB comments for notes that you
don’t want to be visible in the HTML generated by the view template.
The ERB templates you’ve looked at so far were generated by the Rails
scaffold when we first created our blog. They’re uncommented, but you can
easily add your own comments. Here’s one:
<%# This code is crazy %>
In addition to notes to the programmer, you can also use ERB comments to remove code temporarily from the page. Add a hash sign (#) after
the first percent sign (%) in any other ERB tag, and the code inside that tag
will not be executed.
Helpers
Helpers are Ruby methods that simplify the code inside your views, making it
easier to read. Rails includes helpers for creating links to resources, formatting numbers, and other common tasks. You can also easily write your own
helper methods.
By using helpers, you can avoid placing too much logic in your view.
If it takes more than a single line of code to display a value, then that code
should probably be in a helper method.
URL Helpers
Create links with the link_to helper method:
link_to 'Show', post
This example generates an HTML link like this:
Show, assuming post has an id of 1.
You can also use the URL and path helpers you saw in the last chapter
to create links:
link_to 'Edit', edit_post_path(post)
This example generates a link like this: Edit.
Views 59
You can also include the HTTP verb to use for the link as well as additional data attributes. Use this for links that change state on the server,
such as a link to destroy a resource. Remember that GET requests should
not be used to change state.
link_to 'Destroy', post, method: :delete,
data: { confirm: 'Are you sure?'}
This example generates a link with data-method="delete" and
data-confirm="Are you sure?". Rails includes the jQuery unobtrusive Java
Script library (jquery_ujs.js) by default. This library uses the method and
confirm attributes to build a hidden form at run-time that creates a confirmation window and then submits the destroy link using a proper DELETE
request. Aren’t you glad you don’t have to do that yourself?
Note
Web browsers are only able to issue GET and POST requests. Rails fakes the DELETE
request by passing a parameter named _method with the value delete. When you update
a record, the PATCH request is handled the same way.
Number Helpers
Rails includes several handy methods for displaying numbers:
number_to_currency
number_to_human
number_to_human_size
number_to_percentage
number_with_delimiter
number_with_precision
Each method accepts a number and returns a string representing that
number with some formatting applied. That formatting is related to the
word at the end of the method.
The number_to_currency method, shown next, rounds the given number
to two decimal places and prepends a dollar sign:
number_to_currency 100
So this example returns "$100.00".
The methods number_to_human and number_to_human_size convert numbers
into easy-to-read string representations.
number_to_human 1000000
number_to_human_size 1024
So these examples return "1 million" and "1 KB", respectively.
60 Chapter 5
Format percentages with number_to_percentage. This method rounds the
number to three decimal places, by default, and appends a percent sign.
You can specify a precision as an option.
number_to_percentage 12.345
number_to_percentage 12.345, precision: 1
These examples return "12.345%" and "12.3%", respectively.
In addition to URL and number helpers, Rails also has built-in helpers
for working with dates and assets such as images, CSS files, and JavaScript
files. Later in this chapter, I cover helpers for creating forms and form fields.
I can’t cover all of the helpers in Rails here, so for now, let’s see how to
add your own helper methods.
Your Own Helpers
You can easily create your own helpers by adding methods to the appropriate file in the app/helpers directory. The Rails scaffold generator has created
a couple of mostly empty files in that directory for you automatically.
Add helpers that are only needed in a single controller to the helper file
for that controller. For example, helpers that are only used in posts views
should be added to the PostsHelper module in app/helpers/posts_helper.rb.
Add helpers used throughout the application to the ApplicationHelper
module in app/helpers/application_helper.rb. Open this file and let’s see how
it works:
module ApplicationHelper
def friendly_date(d)
d.strftime("%B %e, %Y")
end
end
This code defines a new helper method called friendly_date. You can
use this method in any view in your application to format a date for display.
friendly_date Time.new(2014, 12, 25)
This example returns "December 25, 2014". If you later decide to display
dates in a different format throughout your application, you only have to
change this method instead of changing all of your views.
The Posts Index Page
Now that you know more about how views work in Rails, let’s update the
index view to look more like a blog. Go to http://localhost:3000/posts in your
browser to see the index page, shown in Figure 5-1.
Views 61
Figure 5-1: The posts index page
Your blog posts are currently displayed in a table. Open the file app/
views/posts/index.html.erb in your editor:
Listing posts
Title
Body
<% @posts.each do |post| %>
<%= post.title %>
<%= post.body %>
<%= link_to 'Show', post %>
<%= link_to 'Edit', edit_post_path(post) %>
<%= link_to 'Destroy', post, method: :delete,
data: { confirm: 'Are you sure?' } %>
<% end %>
<%= link_to 'New Post', new_post_path %>
This template first creates an HTML table and adds a table header to
the page. It then loops over each post and displays that post’s attributes
in a table row.
62 Chapter 5
A proper blog would display each post title as a heading followed by the
post body in a paragraph. Update the index view to look like this:
Listing posts
<% @posts.each do |post| %>
<%= link_to post.title, post %>
<%= friendly_date post.created_at %>
<%= post.body %>
<%= link_to 'Edit', edit_post_path(post) %>
<%= link_to 'Destroy', post, method: :delete,
data: { confirm: 'Are you sure?' } %>
<% end %>
<%= link_to 'New Post', new_post_path %>
The template still loops over each post as before. Instead of displaying the post attributes in table cells, however, it now shows the title in
a second-level heading and uses the friendly_date helper you added in
the previous section to format the created_at date. The links to edit and
destroy the post are now at the bottom, and the link to show the post is now
around the post title. Refresh the page in your browser to see the changes,
shown in Figure 5-2.
Figure 5-2: The updated posts index page
Our blog still won’t win any design awards, but it’s looking better!
Views 63
Layouts
You may have noticed that the views you’ve seen so far only include the contents of the web page without the other required elements such as html, head,
and body. These elements are the basic structure of all web pages.
Check the server output in your terminal to see what’s happening when
you load the index page:
--snip- Started GET "/posts" for 127.0.0.1 at 2014-03-09 18:34:40 -0500
Processing by PostsController#index as HTML
Post Load (0.2ms) SELECT "posts".* FROM "posts"
Rendered posts/index.html.erb within layouts/application (62.5ms)
Completed 200 OK in 92ms (Views: 91.2ms | ActiveRecord: 0.2ms)
--snip--
Here, we have a GET request for the path /posts. It is processed by
the index method in PostsController. Finally, the server renders posts/
index.html.erb within layouts/application .
In Rails, a layout is a file containing the basic HTML required for
every page on your site. Rather than repeat the same HTML in every view,
you only write it once inside the layout file. This is another way that Rails
removes needless duplication.
Let’s jump right in and dissect the layout for your blog. The server output calls it layouts/application, so open app/views/layouts/application.html.erb
to see the layout for your application:
Blog
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application',
'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
<%= yield %>
This file contains the basic HTML for every page on your site: the
HTML5 doctype followed by the head section and body section.
The head section sets the title of the page. It then includes Rails helpers
for linking to your site’s CSS and JavaScript files. It also includes a
helper that protects your application from cross-site request forgery (CSRF)
attacks, which I’ll cover in Chapter 11. The body section includes the yield
statement .
64 Chapter 5
The rest of this section covers these helper methods and the yield
statement.
Asset Tag Helpers
In a Rails application, files such as CSS, JavaScript, and images are called
assets. Assets are external files needed by the web browser accessing your
application. These files are stored in subdirectories of the app/assets
directory.
As your application grows, you may need several CSS and JavaScript
files to control your site’s appearance and client-side functionality. The
Rails server output also lists the CSS and JavaScript files your application
is already using:
--snip- Started GET "/assets/scaffolds.css?body=1" for 127.0.0.1 at ...
Started GET "/assets/application.css?body=1" for 127.0.0.1 at ...
Started GET "/assets/turbolinks.js?body=1" for 127.0.0.1 at ...
Started GET "/assets/jquery.js?body=1" for 127.0.0.1 at ...
Started GET "/assets/posts.js?body=1" for 127.0.0.1 at ...
Started GET "/assets/jquery_ujs.js?body=1" for 127.0.0.1 at ...
Started GET "/assets/application.js?body=1" for 127.0.0.1 at ...
Started GET "/assets/posts.css?body=1" for 127.0.0.1 at ...
As you can see, our simple blog is already using three different CSS
files and five JavaScript files . Rather than list each of these files separately in the layout, Rails uses CSS and JavaScript files called manifests to
require individual CSS and JavaScript files. A manifest file is simply a list
of other files needed by your application.
A Rails feature known as the asset pipeline combines these CSS and
JavaScript files together into two files and compresses them when your
application is running in the production environment. These files are
named application.css and application.js. By combining these files, your
application receives fewer requests from users, which should improve its
performance.
The head section of the layout contains ERB tags for adding the CSS
and JavaScript manifest files that your application needs.
stylesheet_link_tag
The stylesheet_link_tag method adds an HTML link tag for the default CSS
manifest, application.css, and each of the CSS files referenced in the manifest. Open the file app/assets/stylesheets/application.css to see how it works.
Views 65
/*
--snip-*
*= require_tree .
*= require_self
*/
This file starts with a block of comments explaining its purpose as
well as the lines starting with require_tree and require_self . The
require_tree . statement includes all other CSS files in the app/assets/
stylesheets directory and subdirectories. The require_self statement means
the contents of this CSS file are included at the bottom.
javascript_include_tag
The javascript_include_tag method adds a script tag for the default JavaScript
manifest, application.js, and each of the JavaScript files listed in the manifest. Now open the JavaScript manifest app/assets/javascript/application.js.
--snip-//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
This file is similar to the CSS manifest. It starts with a block of comments explaining its purpose and then includes the JavaScript libraries,
jquery, jquery_ujs, and turbolinks by default, as well as any other JavaScript
files in the app/assets/javascript directory and subdirectories.
N OTE
The asset pipeline, turbolinks, and other performance issues are discussed in more
detail in Chapter 12.
CSRF Meta Tags Helper
The csrf_meta_tags method adds two meta tags to the head of each web page.
These tags are designed to protect your application from CSRF attacks.
If you view the source on any page of your application, you should see
a meta tag named csrf-token that contains a long string of random hexadecimal digits. This token is unique to your current session and is passed to
your application any time a form is submitted.
66 Chapter 5
In a CSRF attack, a trusted user of your application visits a malicious
site. The malicious site then attempts to submit requests to your application
as that trusted user. Because the malicious site has no way of knowing this
secret token, these requests fail. CSRF and other security concerns are covered in Chapter 11.
Yield
In a layout, the yield statement identifies where content from the view
should be inserted. In this case, the HTML generated by app/views/posts/
index.html is inserted between the body tags to form the complete web page
that is sent to the user.
The yield statement is not required to be the only statement in the body
element. You can add other elements to the body as needed. For example,
you might add a common header or footer here that appears on each page
of your application.
Partials
Like helpers, partials are used to extract code into meaningful units and to
avoid duplicating code that is common to multiple views. The difference
is that whereas helpers contain shared Ruby code, partials contain shared
HTML code.
Partials are stored in view templates with filenames that begin with an
underscore. For example, app/views/posts/_form.html.erb is a partial that renders a post form.
Code that is repeated across multiple pages is commonly separated out
into partials to make the template code easier to follow. If you look at the
new post and edit post templates, app/views/posts/new.html.erb and app/views/
posts/edit.html.erb, respectively, you’ll see they both render the same form
partial with this line of code:
<%= render 'form' %>
Here, the partial is named _form.html.erb but is referred to simply as form
when rendered.
If you find yourself repeating the same HTML code on more than one
page, or in more than one place on a single page, you should copy that code
into a partial and replace it with a render statement.
Collections
Partials can also be used to eliminate loops in view templates. When you
use the :collection option, a corresponding partial is inserted into the template for each member of the collection. Using :collection doesn’t necessarily remove code duplication entirely, but it can simplify the template.
Views 67
For example, you could move the code inside the <% @posts.each ... %>
block in index.html.erb into a new file named app/views/posts/_post.html.erb.
You could then replace the block with a single line of code like this one:
<%= render :partial => 'post', :collection => @posts %>
In this example, Rails understands that @posts is an array of post objects,
so it looks for a partial named app/views/posts/_post.html.erb and renders it
on the page once for each object in the array. Because this action is so common, you can simplify even further to this:
<%= render @posts %>
Let’s get some hands-on experience with partials by adding comments
to the post show page.
Showing Comments
You added a model for comments in Chapter 3 and a controller in Chapter 4,
but you still can’t see them on the page. Nearly every post should have comments, and you don’t want to repeat that code in every single page, so this is
a perfect opportunity to put partials to work.
To get started, open app/views/posts/show.html.erb in your editor:
<%= notice %>
Title:
<%= @post.title %>
Body:
<%= @post.body %>
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
Let’s first clean up this page a bit like we did the posts index page by wrapping the title in a heading tag and the body in a paragraph, as shown here:
<%= notice %>
<%= @post.title %>
<%= @post.body %>
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
68 Chapter 5
Now add a heading and a render statement for the comments at the
bottom of the page:
--snip-Comments
<%= render @post.comments %>
This code shows the comments under the heading by rendering the
@post.comments collection with a partial. For this to work, you’ll also need to
create a partial for rendering a single comment. Create a new file named
app/views/comments/_comment.html.erb containing this:
<%= comment.author %> said:
<%= comment.body %>
If you added any comments earlier using the Rails console, you should
now see them at the bottom of the page. Of course, you can’t ask your users
to add comments using the console; they expect a comment form. Let’s see
how forms are created in a Rails application.
Forms
Accepting input from users can be one of the more difficult parts of building a web application. Rails includes an elegant system for generating forms.
Rails provides helper methods for the various form controls. When
bound to a model, these helper methods generate the correct HTML
markup for passing values back to the controller automatically.
Go to http://localhost:3000/posts/new in your browser to see the New Post
form created by the Rails scaffold generator, as shown in Figure 5-3.
Figure 5-3: The New Post form
Views 69
This simple form consists of a text box for the post title, a text area for
the post body, and a button labeled Create Post to submit the form.
Form Helpers
You can use helpers to generate a form and all of the necessary fields and
labels. Open the file app/views/posts/_form.html.erb to see an example of a
Rails form:
<%= form_for(@post) do |f| %>
<% if @post.errors.any? %>
<%= pluralize(@post.errors.count, 'error') %>
prohibited this post from being saved:
<% @post.errors.full_messages.each do |msg| %>
- <%= msg %>
<% end %>
<% end %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :body %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>
This partial is used when creating a new post and editing an existing
post. The form begins with a call to the form_for method with a block that
contains the rest of the form. Next an if statement checks to see if the
post contains any errors. If the form has errors, the error_explanation div
appears before the rest of the form. Otherwise, nothing is shown here.
Finally, you’ll see the form controls .
Form Errors
Let’s first look at the code for displaying errors like the one shown in
Figure 5-4. Remember from our discussion of controllers that if a create
or update action fails, the form will be rendered again. Try creating a new
post with a blank title to see the error.
70 Chapter 5
Figure 5-4: Post creation error
Figure 5-4 shows the error_explanation div with the number of errors
in a heading followed by a bulleted list of the actual errors. Also, the label
for the title field now has a red background and the text box for the title
is outlined in red. Rails does this by wrapping these elements in a div with
class field_with_errors.
Now that you know how to display errors, let’s look at the form_for
method and other helper methods for creating form controls.
Form Controls
Use the form_for block to create a form bound to a model. For example, this
particular form is bound to the model stored in @post:
<%= form_for(@post) do |f| %>
Within this block, you have access to helper methods to add controls
such as labels, text fields, and buttons to the form. Use the form builder object
(in this case f) to call these methods.
<%= f.label :title %>
The label helper is used to create a label tag for the specified field.
The previous statement will generate this HTML: . Rails converts the field name to a string and capitalizes the
first letter. Your users can click this label to focus the cursor in the text field
for the title. Of course, you still have to create that text field, and Rails has a
helper for that, too.
<%= f.text_field :title %>
Views 71
The text_field helper generates the following HTML: . Note that the id of this input
(post_title) matches the for value of the label tag in the previous paragraph. Also notice the name of this field. Rails sets names on form fields
to indicate both the model (post) and the attribute to modify (title).
The next few lines of code add a label for the post body followed by a
text_area for entering the body text. These controls work the same as the
title fields. The text_area helper generates this HTML: .
Besides controls for entering the title and body text, you need a button
to submit the form:
<%= f.submit %>
The submit helper generates a submit button. The button’s label is based
on the class name of the current model and whether the model has been
saved to the database. In the case of a new post, the value will be "Create
Post" and the HTML looks like this: . If the post has already been saved to the database,
the value is "Update Post".
Rails includes form helpers for every field you need, and you can always
add your own helpers to create custom fields. Built-in examples include
check_box, hidden_field, password_field, radio_button, and text_area.
Helper methods for HTML5 field types, such as email_field, phone_field,
and url_field, are also included. These fields look like regular text fields,
but on mobile devices, you’ll see an alternate keyboard. Use these field
types to ensure that your application is mobile-friendly.
Comment Form
Now let’s put your new form knowledge to work and add the comment form.
First, add another heading to the end of the post show page at app/views/
posts/show.html.erb:
New Comment
Add the form for creating a comment underneath that new heading, as
shown next. The array being passed to the form_for method contains both
@post and @post.comments.build. Because every comment belongs to a post,
you must pass the post and comment to the method. In this case, you’re
using the current post and a new comment created by @post.comments.build.
<%= form_for [@post, @post.comments.build] do |f| %>
<%= f.label :author %>
<%= f.text_field :author %>
72 Chapter 5
<%= f.label :body %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>
The rest of the comment form should look similar to the post form;
even the field names are the same. Refresh the page in your browser and
make sure the form renders like the one shown in Figure 5-5.
Figure 5-5: The New Comment form
Now enter an author name and comment body and click the Create
Comment button. Submitting the form should display your new comment
and add a flash message to the top of the page that says “Comment was successfully created.”
Check the output of the rails server command in your terminal to see
exactly what happened. Assuming your post has an id of 1, you should first see
a POST to the path /posts/1/comments. This calls the CommentsController#create
method.
You added this controller and method in the last chapter; recall that
the create method builds and saves a new comment and then redirects the
user back to the post. You should see this redirect as a GET request for
/posts/1 in the output. This happens when the user is redirected back to the
post show page.
Views 73
Summary
Spend some time working on your application’s views. We cleaned up the
index page a little, but I recommend you improve it further. The other
pages could also use some work. The following exercises should give you
some ideas.
In the next chapter, you’ll set up Git for version control and deploy your
application to the web for everyone to see.
Exercises
1.
2.
3.
Our blog’s heading only appears on the index page. Move the h1 element from the posts index page to the application layout. While you’re
at it, come up with something a little more interesting to call it than
“Listing posts.” Also, change the h1 headings on the New Post and Edit
Post pages to h2 headings.
In Chapter 3, you added an author field to the posts table. Add a text
field for author to the post form and update the post_params method in
PostsController to permit author as a parameter.
Users can create comments now, but you have no way to remove them.
You need to be able to remove the inevitable spam posts! First, update
the comment resource in config/routes.rb to add a route for the destroy
action. The :only option should be :only => [:create, :destroy]. Next,
add the destroy action in the CommentsController, similar to the destroy
action for posts. Finally, add a link to this action at the bottom of app/
views/comments/_comment.html.erb:
<%= link_to 'Destroy', [comment.post, comment],
method: :delete, data: { confirm: 'Are you sure?' } %>
74 Chapter 5
6
De ploy me n t
Now that you’ve built an application, let’s put it on
the Web for everyone to see. Rails applications can be
deployed in many ways. Rails runs on everything from
simple shared hosting to dedicated servers to virtual
servers in the cloud.
The cloud application platform known as Heroku is one of the easiest
ways to deploy your application, and I cover it in this chapter. Heroku uses
the Git version control system to deploy applications, so we need to talk
about version control systems first.
Version Control
A version control system (VCS) records changes to files over time so you can
easily go back to a specific version later. The repository is the data structure,
usually stored on a server, that holds a copy of the files in the VCS and a historical list of changes to those files. With a VCS, you can make changes to your
source code knowing that you can always go back to the last working version.
Originally, version control systems were centralized. That is, the source
code repository was stored on a single server. Developers could connect to
that server and check out files to make changes to the code. But centralized systems also have a single point of failure. Examples of centralized
version control systems include the Concurrent Version System (CVS) and
Subversion.
The most popular type of version control system today is distributed.
With a distributed version control system, each client stores a complete copy
of the source code repository. That way, if a single client fails, everyone else
can continue to work with no loss of data.
In a distributed system, a central server is still commonly used. Developers
push their changes to this server and pull changes made by other developers. Popular distributed version control systems include Git and Mercurial.
Because Heroku uses Git to deploy applications, I’ll focus on Git.
Git
Git was originally developed by Linus Torvalds in 2005 for use with the
Linux kernel. The word git is British slang for a despicable person. Torvalds
once joked that he names all of his projects after himself.
Git quickly spread beyond the Linux community, and most Ruby projects
now use Git, including Ruby on Rails. If you don’t already have Git, installation instructions can be found in “Ruby, Rails, and Git” on page xxi.
Setup
Before you start using Git, set your name and email address. Open a terminal window and enter the following command to set your name:
$ git config --global user.name "Your Name"
The --global flag tells Git to apply this change to your global configuration. Without this flag, the change would only apply to the current repository.
Also, set your email address:
$ git config --global user.email "you@example.com"
Now every time you commit a change, your name and email address
is included, making it easy to see who made which changes when working
with a team.
Getting Started
Now you’re ready to create a repository for the blog. Move to your code/blog
directory and enter this command:
$ git init
Initialized empty Git repository in /Users/tony/code/blog/.git/
76 Chapter 6
This initializes an empty Git repository in the hidden .git subdirectory.
Next, let’s add all of the application’s files to the repository:
$ git add .
The add command accepts a filename or directory path and adds it to
Git’s staging area. Files in the staging area are ready to be committed to the
repository. The dot in the command represents the current directory. So after
you run this command, all files in the current directory and any subdirectories are ready to be committed. When you commit, Git takes a snapshot of
the current state of your project and stores it in the repository.
Now commit all staged files to the repository:
$ git commit -m "Initial commit"
[master (root-commit) e393590] Initial commit
85 files changed, 1289 insertions(+)
create mode 100644 .gitignore
create mode 100644 Gemfile
--snip-create mode 100644 test/test_helper.rb
create mode 100644 vendor/assets/javascripts/.keep
create mode 100644 vendor/assets/stylesheets/.keep
Note that I specified the commit message “Initial commit" with the
-m flag . If you leave off this flag, Git will open your default editor so you
can type a commit message. If you do not type a commit message, the
commit fails.
If you want to view the current repository’s commit history, enter the
git log command. The list shows previous commits in order from newest to
oldest. Each entry includes who made the commit and when, along with the
commit message.
$ git log
commit e3935901a2562bf8c04c480b3c5681c102985a4e
Author: Your Name
Date:
Wed Apr 2 16:41:24 2014 -0500
Initial commit
Each commit is represented by a unique 40-character hexadecimal
hash . These hashes can be abbreviated to the first seven characters—in
this case, e393590—if you need to refer to this particular commit again.
Basic Usage
As you work on a project using Git, follow this basic workflow:
1.
2.
3.
Edit local files as needed.
Stage files to be committed with the git add command.
Commit the changes to the repository with the git commit command.
Deployment 77
You can commit changes to Git as often as you like, but I find it helpful
to commit changes related to a single simple feature or bug fix together.
That way, all of the changes are tied to one commit, making it easier to
revert and remove a feature if necessary. It’s also a good idea to commit any
outstanding changes at the end of a working session.
Other Useful Commands
Git contains many additional commands; enter git --help to see a list of
those you’ll use most often. You’ve already seen the init, add, commit, and log
commands, but here are a few more that you’ll find particularly useful as
you navigate Git.
The git status command displays a list of changed and new files:
$ git status
On branch master
nothing to commit, working directory clean
In this case, nothing has changed. Edit a file in your project, README
.rdoc, for example, and then enter the git status command again:
$ git status
On branch master
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes...)
modified:
README.rdoc
no changes added to commit (use "git add" and/or "git commit -a")
The git status command shows the current state of your working directory and staging area. Here, it lists all files that have been staged for commit
and files with changes that have not been staged for commit .
The git diff command shows detailed changes to files:
$ git diff
diff --git a/README.rdoc b/README.rdoc
index dd4e97e..c7fabfa 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -1,4 +1,4 @@
-== README
+== Blog
This README would normally document whatever steps are necessary to get the
application up and running.
78 Chapter 6
Here, I changed the word README to Blog on the first line of the
file. Use this command before git add to see exactly what changes will be
staged for commit. You can also pass a filename to this command if you
only care about changes to a single file.
The git checkout command can undo changes to a file:
$ git checkout -- README.rdoc
$ git status
On branch master
nothing to commit, working directory clean
Here, I’ve discarded the changes to the file README.rdoc by using git
checkout followed by two dashes and the filename . This command does
not produce any output. Then I used git status to confirm that the change
had been discarded.
The git clone command makes a local copy of a remote repository:
$ git clone url
The remote repository is represented by . Git is a great tool for collaboration and is used by many open-source projects. This command makes
that possible. Before you start working on an existing project, you clone a
copy of the repository to your computer.
Branches
You may have noticed that the git status command includes the phrase,
“On branch master.” In Git, a branch is a named set of changes. The default
branch is called master. It represents the main line of development. The
changes I’ve made so far have all been committed to the master branch.
If you’re working on a large feature that may take some time to complete, you can create a separate branch to store changes you’re working on
without affecting the master branch. This way, you can work on your own
branch without impacting the rest of your team. Once the new feature is
complete, you’ll merge your new branch back into the master branch.
Use the git branch command followed by a branch name of your choice
to create a new branch. In this example, I’ll call my branch testing :
$ git branch testing
Enter the git branch command without specifying a name to see a list of
the branches that currently exist in the repository:
$ git branch
* master
testing
Deployment 79
The star shows the currently selected branch. I created a new branch,
but I’m still looking at the master branch. To switch to a different branch,
use the git checkout command:
$ git checkout testing
Switched to branch 'testing'
Now I’m on the testing branch. Changes committed here will not affect
the master branch. Once you are finished making changes, checkout the
master branch and merge your changes into it:
$ git checkout master
Switched to branch 'master'
$ git merge testing
Already up-to-date.
All of the changes from the testing branch are now also in the master
branch. You can confirm this with the git log command. Now that you’re
finished with the testing branch, add the -d flag to the git branch command
to delete it:
$ git branch -d testing
Deleted branch testing (was e393590).
You don’t have to delete branches after they have been merged, but
doing so keeps the list of branches clean.
Remotes
So far, all of our changes have been stored locally, but you should store an
additional copy of your repository on another server as a backup and to
make it easier for others to clone your repository. To do this, you need to
set up a remote. A remote is simply a nickname for another repository at a
specific URL. Use the git remote add command to associate a nickname with
a URL:
git remote add name url
Once you have added a remote, use the git push command to send
changes to the URL and the git pull command to retrieve changes made
remotely. You’ll see a real-world example of this in the next section.
80 Chapter 6
Heroku
Heroku is a cloud application platform for deploying web applications. This
type of platform is sometimes referred to as a Platform as a Service (PaaS),
meaning Heroku takes care of server configuration and management so
you can focus on application development. The service also includes an
extensive collection of add-ons. Getting started is free, but large applications requiring more processor resources and memory can get expensive.
After some initial setup, you can use a git push command to deploy
your application and access it on the Web.
Getting Started
First, sign up for a free account at http://www.heroku.com. Remember the
password you select; you’ll need it again to log in.
Next, install the Heroku Toolbelt if you haven’t already (see http://toolbelt
.heroku.com/ for instructions). The Toolbelt is Heroku’s set of tools for deploying your application to its platform.
Now, open a terminal window, navigate to your blog directory, and log
in to Heroku:
$ heroku login
Enter your Heroku credentials.
Email: you@example.com
Password (typing will be hidden):
Authentication successful.
This command prompts you for your email address and the password you
created earlier, and then it checks your computer for an existing secure shell
(SSH) public key. Your public key is one half of the public/private key pair
used to authenticate over SSH. When you attempt to log on, your private
key is used to make a cryptographic digital signature. Heroku then uses
your public key to verify this digital signature and confirm your identity.
If you don’t already have a public key, press Y to create one when
prompted. Your public key is automatically uploaded to Heroku after it is
created. Heroku uses your public key for authentication so you don’t have
to type your password every time you deploy your application.
Now that you’ve logged in to Heroku, you need to prepare your application for deployment.
Updating Your Gemfile
No matter what kind of application you’re building, you need to install
certain gems to interface with Heroku and deploy your application. In this
section, we’ll look at the two gems you need to add to your application’s
Gemfile.
Deployment 81
Heroku’s servers use the PostgreSQL database server. Rather than
install PostgreSQL locally, we used SQLite for development. You’ll need to
ensure that the PostgreSQL gem, called simply pg, is installed in the production environment.
Heroku also requires the rails_12factor gem, which ensures that your
application’s assets can be served by Heroku’s servers and that your application’s log files are sent to the correct place.
Open the file Gemfile in the root of your Rails application and locate the
line gem 'sqlite3'. You’ll use the PostgreSQL gem in production, but you
still need the SQLite gem for development and testing, so update this line
by adding group: [:development, :test] as shown here:
gem 'sqlite3', group: [:development, :test]
This instructs the bundle command to install this gem only in the development and test environments.
Now you need to install the pg and rails_12factor gems just mentioned.
You only need these gems in the production environment, so add these
next lines below the line you just updated:
# gems required by Heroku
gem 'pg', group: :production
gem 'rails_12factor', group: :production
Once you’ve made these changes, save and close the Gemfile. Because
you’ve changed your application’s Gemfile, run the bundle command again
to update dependencies.
$ bin/bundle install --without production
Because you’re running this command locally, where you develop and
test your application, you don’t need to install production environment
gems, so add the --without production flag. Bundler remembers flags passed
to bundle install, so --without production is assumed every time you run the
command from now on.
Finally, you need to add and commit these changes to your Git repository. Enter these commands to update Git with your changes:
$ git add .
$ git commit -m "Update Gemfile for Heroku"
[master 0338fc6] Update Gemfile for Heroku
2 files changed, 13 insertions(+), 1 deletion(-)
You could enter any message in place of Update Gemfile for Heroku, but
commit messages are more helpful when they describe what you’ve changed.
82 Chapter 6
Now, your account is set up, and your application is nearly ready to
deploy. The last step is to create an application on Heroku:
$ heroku create
Creating glacial-journey-3029... done, stack is cedar
http://glacial-journey-3029.herokuapp.com/ | git@he...
Git remote heroku added
This command creates a new application on Heroku’s servers with a
randomly generated name. You could have specified a name after the create
command, but the name must be unique. You can always change the name
later if you want. The create command also sets up a Git remote named
heroku for you automatically.
Deploying Your Application
Everything is ready now, so you can finally deploy your application. Use
the git push command to push the current state of your master branch to
Heroku:
$ git push heroku master
Initializing repository, done.
Counting objects: 102, done.
Delta compression using up to 8 threads.
--snip------> Launching... done, v6
http://glacial-journey-3029.herokuapp.com/ deployed to Heroku
To git@heroku.com:glacial-journey-3029.git
* [new branch]
master -> master
Heroku recognizes this git push command and automatically detects
that a Ruby on Rails application is being deployed, installs the production
gems specified in your Gemfile, updates your application’s database configuration, precompiles your application’s assets, and launches your application.
When you deploy any application for the first time, you also need to run
database migrations to create the database tables needed by your application in Heroku’s database server. Use the heroku run command to execute
the rake db:migrate command on Heroku’s server:
$ heroku run rake db:migrate
Running `rake db:migrate` attached to terminal... up, run.1833
Migrating to CreatePosts (20140315004352)
--snip--
If you make more database changes to your application, remember to
commit the changes to the master branch in Git, push the master branch to
Heroku, and run this command again.
Deployment 83
Now you can open your web browser to the URL Heroku created for you
earlier, or you can let Heroku handle that for you by entering this command:
$ heroku open
Your default web browser should open and load your blog application
automatically.
Now that your application is set up on Heroku, you can deploy any time
you want by committing changes to your Git repository and pushing the
changes to Heroku.
Git Hub
Any discussion of Git in a Rails book is incomplete without at least a mention
of GitHub. GitHub is the number one source code host in the world. GitHub
provides project management features such as wikis, issue tracking, and code
review via pull requests.
The Rails community has embraced GitHub as the best place for collaborating on open-source software. Rails itself is hosted on GitHub at https://github
.com/rails/rails/. Sign up for a free account, if you don’t already have one, and
join the community!
Summary
Your blog is now safely stored in the Git distributed version control system.
Changes to your source code are being tracked and can be easily undone.
You blog is also available to the world via Heroku. Now you can deploy new
features with a git push command.
Part I Remarks
This chapter marks the end of the first part of this book. We’ve covered the
fundamentals of Ruby and Rails. Models represent your application’s data;
views are the user interface for your application; and controllers are the
glue that holds them together. You’ll use these concepts to build any application you want.
Looking at the application you built in Part I, you’ll find plenty of areas
to improve. For example, anyone can edit or even delete posts on your blog.
Also, what happens if you write thousands of posts? The index page will
probably time out before it can display them all! You may not quite have the
tools to fix those problems right now, but once you dive into Part II, that
will change.
84 Chapter 6
In the next part of this book, we’ll build a new social network application
and cover advanced topics such as more complex data modeling, authentication, testing, security, performance optimizations, and debugging.
After learning these concepts, you’ll be able to solve these problems
with the blog and build a variety of other applications.
Exercises
1.
2.
3.
Practice making changes to your application, adding and committing those changes to your local Git repository, and then pushing the
changes to Heroku. Many Rails developers deploy multiple times per
day, so familiarize yourself with this process.
Create an account on GitHub, learn how create a new repository on
its servers, and push your application. GitHub has an online help area
that walks you through the process if you have any trouble. Also, use
GetHub’s Explore feature to see the repositories of popular projects on
its servers.
Finally, see if you can “scratch your own itch.” Create a simple Rails
application based on one of your interests. Create a catalog of your
favorite books, or maybe an application to track your vinyl collection.
Deployment 85
Part II
B u i l d i ng a
S o c i a l N e t w o r k i ng A p p
7
Adv a nc e d R u b y
You learned the fundamentals of Ruby back in
Chapter 1. This chapter covers some of the language’s advanced features, including modules,
the Ruby object model, introspection, and a bit of
metaprogramming.
Modules are used frequently in Rails applications to group similar functionality and share behavior between classes. The Ruby object model determines how methods are found and called in a hierarchy of inherited classes
and shared code from modules. Introspection supports polymorphism by
allowing you to look inside a class to see which methods it understands.
Metaprogramming lets your classes respond to methods that don’t exist by
defining methods at runtime.
Open a terminal window and launch IRB to get started. Several of the
examples in this chapter are longer than normal. You may find it easier to
type the example into your editor, save it as a file with the extension rb, and
then run the example in your terminal by entering ruby filename.rb. Or you
can simply copy and paste the code from your editor into IRB.
Modules
As you saw in Chapter 1, a module is a collection of methods and constants
that cannot be instantiated. You define modules in basically the same way
you define classes. Module definitions begin with the word module, followed
by an uppercase name, and continue to the word end.
To demonstrate using modules, we first need a class definition. Let’s
define a simple Person class:
class Person
attr_accessor :name
def initialize(name)
@name = name
end
end
This class uses attr_accessor to define getters and setters for the instance
variable @name, and sets the value of @name when created v.
Class names are usually nouns because they represent objects. Module
names are usually adjectives because they represent behavior. Many Ruby
modules take this convention a step further and use adjective names ending
with able, such as Comparable and Forwardable.
Here’s a silly example, just to show how it’s done:
module Distractable
def distract
puts "Ooh, kittens!"
end
end
Enter this module in IRB, include it in the Person class you created earlier in this chapter, and see if you can distract someone:
irb(main):001:0> class Person
irb(main):002:1> include Distractable
irb(main):003:1> end
=> Person
irb(main):004:0> p = Person.new("Tony")
=> #
irb(main):005:0> p.distract
Ooh, kittens!
=> nil
In Chapter 5, you also defined a module method while working with
Rails helpers. ApplicationHelper is a module that is automatically mixed into
all controllers by Rails.
90 Chapter 7
Modules serve two purposes in Ruby:
•
•
Modules are used to group related methods and prevent name
conflicts.
Modules define methods that can be mixed in to classes to provide
additional behavior.
Organizing your code becomes more important as your application
grows. By providing namespaces and making it easy to share code between
classes, modules help you break your code into manageable pieces. Let’s
look at both of these purposes.
Modules as Namespaces
A Ruby module can be used as a namespace, a container for code such as
constants or methods with related functionality.
The Math module is an example of a built-in Ruby module used as a
namespace. It defines the constants E and PI as well as many common trigonometric and transcendental methods. The double-colon operator (::) is
used to access constants in Ruby. The following example accesses the constant PI in the Math module:
irb(main):006:0> Math::PI
=> 3.141592653589793
Methods defined in a module are accessed with a dot (.), just like
methods in a class:
irb(main):007:0> Math.sin(0)
=> 0.0
Modules as Mixins
A Ruby module can also be used as a mixin to provide additional functionality to a class. Ruby only supports single inheritance; that is, a class can only
inherit from a single parent class. Modules allow you to implement something similar to multiple inheritance: a class can include several modules,
adding each module’s methods to its own.
You can add a module’s methods to a class in three ways, using include,
prepend, or extend. I discuss the effect of each of these keywords next.
include
The include statement adds the methods from a module to a class as instance
methods and is the most common way of mixing a module into a class.
Advanced Ruby 91
The Comparable module, included in Ruby, is commonly used as a mixin.
It adds comparison operators and the between? method to classes when
included. The class only needs to implement the <=> operator. This operator compares two objects and returns –1, 0, or 1, depending on whether the
receiver is less than, equal to, or greater than the other object.
To use this module as a mixin, add it Person class you created previously:
class Person
include Comparable
def <=>(other)
name <=> other.name
end
end
This class now includes the Comparable module and defines the
<=> operator to compare the name of this object with the name of
another object.
After entering this in IRB, create a few people and see if they can be
compared:
irb(main):008:0> p1 = Person.new("Tony")
=> #
irb(main):009:0> p2 = Person.new("Matt")
=> #
irb(main):010:0> p3 = Person.new("Wyatt")
=> #
irb(main):011:0> p1 > p2
=> true
Here p1 is greater then p2 because T is greater than M alphabetically.
The between? method tells you whether an object falls between two others:
irb(main):012:0> p1.between? p2, p3
=> true
In this case, between? returns true since T is between M and W alphabetically, which means it works as expected.
prepend
The prepend statement also adds a module’s methods to a class, but prepend
inserts the module’s methods before the class’s methods. This means if the
module defines a method with the same name as the class, the module’s
method will be executed instead of the class’s method. Using prepend, you
can override a method in the class by writing a method in the module with
the same name.
One practical use for prepend is memoization. Memoization is an optimization technique in which a program stores the result of a calculation to
avoid repeating the same calculation multiple times.
92 Chapter 7
For example, imagine you wanted to implement the Fibonacci sequence
in Ruby. The first two numbers in the Fibonacci sequence are zero and one.
Each subsequent number is the sum of the previous two. Here is a method
to calculate the nth value of the Fibonacci sequence in Ruby:
class Fibonacci
def calc(n)
return n if n < 2
return calc(n - 1) + calc(n - 2)
end
end
Notice that the calc method is recursive. Every call to calc with a value
of n greater than 1 results in two more calls to itself . Try creating an
instance of this class and calculating some small values of n:
irb(main):013:0> f = Fibonacci.new
=> #
irb(main):014:0> f.calc 10
=> 55
irb(main):015:0> f.calc 30
=> 832040
As you call the method with larger values of n, the method takes noticeably longer to run. For values of n around 40, the method takes several seconds to return an answer.
The Fibonacci calc method is slow because it repeats the same calculations many times. But if you define a module to implement memoization,
the calculations should take significantly less time. Let’s do that now:
module Memoize
def calc(n)
@@memo ||= {}
@@memo[n] ||= super
end
end
The Memoize module also defines a calc method. This method has a couple of interesting features. First, it initializes a class variable named @@memo
with an empty hash if it is not already initialized. This hash stores the result
of the calc method for each value of n. Next, it assigns the return value of
super to @@memo at key n if that value is not already assigned. Because we
are using prepend to add this module into Fibonacci, super calls the original
calc method defined by the class.
Each time the calc method is called, @@memo stores the Fibonacci number for the value n. For example, after calling calc(3), the @@memo hash holds
these keys and values:
{
0 => 0,
1 => 1,
Advanced Ruby 93
2 => 1,
3 => 2
}
On each line, the key (the first number) is the value of n and the
value (the second number) is the corresponding Fibonacci number. The
Fibonacci number for 0 is 0, 1 is 1, 2 is 1, and 3 is 2. By storing these intermediate values, the calc method never needs to perform the same calculation more than once. Use prepend Memoize to add the Memoize module to the
Fibonacci class and try it for yourself:
irb(main):016:0>
irb(main):017:1>
irb(main):018:1>
=> Fibonacci
irb(main):019:0>
=> 102334155
class Fibonacci
prepend Memoize
end
f.calc 40
Now that the values of calc are being memoized, you should be able
to call calc for greater values of n and get an answer almost instantly. Try
it with n = 100 or even n = 1000. Note that you didn’t have to restart IRB or
instantiate a new Fibonacci object. Method lookup in Ruby is dynamic.
extend
When you use include or prepend to add a module to a class, the module’s
methods are added to the class as instance methods. In Chapter 1, you
learned that there are also class methods that are called on the class itself
instead of on an instance of the class. The extend statement adds the methods from a module as class methods. Use extend to add behavior to the class
itself instead of instances of the class.
The Ruby standard library includes a module named Forwardable, which
you can use to extend a class. The Forwardable module contains methods
useful for delegation. Delegation means relying on another object to handle
a set of method calls. Delegation is a way to reuse code by assigning the
responsibility of certain method calls to another class.
For example, imagine a class named Library that manages a collection
of books. We store the books in an array named @books:
class Library
def initialize(books)
@books = books
end
end
We can store our books, but we can’t do anything with them yet. We
could use attr_accessor to make the @books array available outside of the
class, but that would make all of the array’s methods available to users of
our class. A user could then call methods such as clear or reject to remove
all of the books from our library.
94 Chapter 7
Instead, let’s delegate a few methods to the @books array to provide the
functionality we need—a way to get the size of the library and add a book.
require 'forwardable'
class Library
extend Forwardable
def_delegators :@books, :size, :push
def initialize(books)
@books = books
end
end
The Forwardable module is in the Ruby Standard Library, not the
Ruby core, so we first need to require it . Next, we use extend to add the
Forwardable methods to our class as class methods . Finally, we can call
the def_delegators method . The first argument to this method is a symbol
representing the instance variable to which we’re delegating methods.
In this case, the instance variable is @books. The rest of the arguments
are symbols representing the methods we want to delegate. The size method
returns the number of elements in the array. The push method appends a
new element to the end of an array.
In the following example, lib.size initially prints 2 because we have two
books in our library. After adding a book, the size updates to 3.
irb(main):020:0> lib = Library.new ["Neuromancer", "Snow Crash"]
=> #
irb(main):021:0> lib.size
=> 2
irb(main):022:0> lib.push "The Hobbit"
=> ["Neuromancer", "Snow Crash", "The Hobbit"]
irb(main):023:0> lib.size
=> 3
Ruby Object Model
The Ruby object model explains how Ruby locates a method when it is called.
With inheritance and modules, you may find yourself wondering exactly
where a particular method is defined or, in the case of multiple methods
with the same name, which one is actually invoked by a particular call.
Ancestors
Continuing with the simple Person class defined previously, we can find out
a lot about this class in IRB. First, let’s see which classes and modules define
methods for the Person class:
irb(main):024:0> Person.ancestors
=> [Person, Distractable, Comparable, Object, Kernel, BasicObject]
Advanced Ruby 95
The class method ancestors returns a list of classes that Person inherits from and the modules it includes. In this example, Person, Object, and
BasicObject are classes, whereas Distractable, Comparable, and Kernel are modules. You can find out which of these are classes and which are modules by
calling the class method as explained in the Class section below.
Object is the default root of all Ruby objects. Object inherits from
BasicObject and mixes in the Kernel module. BasicObject is the parent class
of all classes in Ruby. You can think of it as a blank class that all other
classes build on. Kernel defines many of the Ruby methods that are called
without a receiver, such as puts and exit. Every time you call puts, you’re
actually calling the instance method puts in the Kernel module.
The order of this list indicates the order in which Ruby searches for a
called method. Ruby first looks for a method definition in the class Person
and then continues looking through the list until the method is found. If
Ruby doesn’t find the method, it raises a NoMethodError exception.
Methods
You can see a list of the class methods and instance methods defined by a
class by calling methods and instance_methods, respectively. These lists include
methods defined by all parent classes by default. Pass the parameter false to
leave out only these:
irb(main):025:0> Person.methods
=> [:allocate, :new, :superclass, :freeze, :===, :==, ... ]
irb(main):026:0> Person.methods(false)
=> []
irb(main):027:0> Person.instance_methods(false)
=> [:name, :name=, :<=>]
The Person class contains almost 100 different class methods from its
ancestors, but it defines none of its own, so the call to methods(false) returns
an empty array. The call to instance_methods returns the name and name= methods defined by attr_accessor and the <=> method that we defined in the
body of the class.
Class
The last piece of the object model concerns the Person class itself. Everything
in Ruby is an object, that is, an instance of a class. Therefore, the Person
class must be an instance of some class.
irb(main):028:0> Person.class
=> Class
All Ruby classes are instances of the class Class. Defining a class, such
as Person, creates an instance of the class Class and assigns it to a global constant, in this case Person. The most important method in Class is new, which is
responsible for allocating memory for a new object and calling the initialize
method.
96 Chapter 7
Class has its own set of ancestors:
irb(main):029:0> Class.ancestors
=> [Class, Module, Object, Kernel, BasicObject]
Class inherits from the class Module, which inherits from Object as before.
The Module class contains definitions of several of the methods used in this
section such as ancestors and instance_methods.
Introspection
Introspection, also known as reflection, is the ability to examine an object’s
type and other properties as a program is running. You’ve already seen how
to determine an object’s type by calling class and how to get a list of methods defined by an object by calling methods and instance_methods, but Ruby’s
Object class defines several more methods just for introspecting objects. For
example, given an object, you may want to determine if it belongs to a particular class:
irb(main):030:0> p = Person.new("Tony")
=> #
irb(main):031:0> p.is_a? Person
=> true
The is_a? method returns true if the given class is the class of the receiving object. In this case, it returns true because the object p is a Person.
irb(main):032:0> p.is_a? Object
=> true
It also returns true if the given class or module is an ancestor of the
receiving object. In this case, Object is an ancestor of Person, so is_a?
returns true.
Use the instance_of? method if you need to determine exactly which
class was used to create an object:
irb(main):033:0> p.instance_of? Person
=> true
irb(main):034:0> p.instance_of? Object
=> false
The instance_of? method returns true only if the receiving object is an
instance of the given class. This method returns false for ancestors and
classes inheriting from the given class. This type of introspection is helpful in some situations, but generally you don’t need to know the exact class
used to create an object—just the object’s capabilities.
Advanced Ruby 97
Duck Typing
In duck typing, you only need to know whether an object accepts the methods you need to call. If the object responds to the needed methods, you
don’t have to worry about class names or inheritance. The name duck typing comes from the phrase, “If it walks like a duck and quacks like a duck,
call it a duck.”
In Ruby, you can use the respond_to? method to see if an object responds
to a particular method. If respond_to? returns false, then calling the method
raises a NoMethodError exception as explained earlier.
For example, imagine a simple method to print some information to a
file with a timestamp:
def write_with_time(file, info)
file.puts "#{Time.now} - #{info}"
end
You can try this method in IRB.
irb(main):001:0> f = File.open("temp.txt", "w")
=> #
irb(main):002:0> write_with_time(f, "Hello, World!")
=> nil
irb(main):003:0> f.close
=> nil
First, open a File named temp.txt in the current directory and store
the File instance in the variable f . Then pass f and the message "Hello,
World!" to the write_with_time method . Finally, close the File with f.close .
The file temp.txt in the current directory now contains a single line similar to the one here:
2014-05-21 16:52:07 -0500 - Hello, World!
This method works great until someone accidentally passes a value to it
that isn’t a file, such as nil. Here’s a possible fix for that bug:
def write_with_time(file, info)
if file.instance_of? File
file.puts "#{Time.now} - #{info}"
else
raise ArgumentError
end
end
This fix solves the problem by checking to see if file is an instance of
the File class , but it also limits the usefulness of this method. Now it only
works with files. What if you want to write over the network using a Socket or
write to the console using STDOUT?
98 Chapter 7
Instead of testing the type of file, let’s test its capabilities:
def write_with_time(file, info)
if file.respond_to?(:puts)
file.puts "#{Time.now} - #{info}"
else
raise ArgumentError
end
end
You know that the write_with_time method calls the method puts, so
check to see if file responds to the puts method . Now, write_with_time
works with any data type that responds to the puts method.
Using duck typing leads to code that can be easily reused. Look for
more opportunities to apply duck typing as you build applications.
Metaprogramming
Metaprogramming is the practice of writing code that works with code instead
of data. With Ruby, you can write code that defines new behavior at runtime.
The techniques in this section can save you time and remove duplication
from your code by allowing Ruby to generate methods when your program
is loaded or as it runs.
This section covers two different ways of dynamically defining methods:
define_method and class_eval. It also covers method_missing, so you can respond
to methods that haven’t been defined.
define_method
Let’s say we have an application with a list of features that can be enabled
for users. The User class stores these features in a hash named @features. If
a user has access to a feature, the corresponding hash value will be true.
We want to add methods of the form can_ feature! and can_ feature? to
enable a feature and check if a feature is enabled, respectively. Rather than
write several mostly identical methods, we can iterate over the list of available features and use define_method, as shown here, to define the individual
methods:
class User
FEATURES = ['create', 'update', 'delete']
FEATURES.each do |f|
define_method "can_#{f}!" do
@features[f] = true
end
define_method "can_#{f}?" do
!!@features[f]
end
end
Advanced Ruby 99
def initialize
@features = {}
end
end
The User class first creates a constant array of available features named
FEATURES. It then iterates over FEATURES using each and calls define_method to
create a method of the form can_ feature! to allow a user access to a feature. Still inside the each block, the class also defines a method of the form
can_ feature? that determines whether a user has access to the feature. This
method converts the value @features[f] to either true or false by using two
NOT operators .
N OTE
Using two NOT operators isn’t strictly necessary because the @features hash returns
nil for keys without values and Ruby treats nil as false, but this technique is commonly used.
Now let’s create a new User and try the dynamically defined methods:
irb(main):001:0> user = User.new
=> #
irb(main):002:0> user.can_create!
=> true
irb(main):003:0> user.can_create?
=> true
irb(main):004:0> user.can_update?
=> false
irb(main):005:0> user.can_delete?
=> false
If you want more practice with define_method, see if you can add methods of the form cannot_feature!, which disables a feature for the user. More
details are provided in Exercise 3 at the end of this chapter.
class_eval
The class_eval method evaluates a string of code as if it were typed directly
into the class definition. Using class_eval is an easy way to add instance
methods to a class at runtime.
When I discussed attr_accessor in Chapter 1, you learned that it defines
getter and setter methods for instance variables in a class, but I didn’t discuss exactly how those methods were defined. The attr_accessor method
is built in to Ruby. You don’t need to define it yourself, but you can learn
about class_eval by implementing your own version of attr_accessor.
class Accessor
def self.accessor(attr)
class_eval "
def #{attr}
@#{attr}
100 Chapter 7
end
def #{attr}=(val)
@#{attr} = val
end
"
end
end
Here, you define a class named Accessor with a single class method
named accessor . This method works like the built-in attr_accessor. It
accepts a single parameter representing the attribute for which you’re creating getter and setter methods. Pass the string to class_eval, which uses
string interpolation to insert the value of attr as needed to define two methods. The first method has the same name as the attribute and returns the
value of the attribute . The second method is the attribute name followed
by an equal sign. It sets the attribute to a specified value val .
For example, if attr is :name, then accessor defines the methods name and
name= by replacing attr with name in the specified places. This is a little hard
to follow without an example. The following code uses the accessor method
in a class:
class Element < Accessor
accessor :name
def initialize(name)
@name = name
end
end
First, you have the Element class inherit from the Accessor class so
the accessor method is available. Then, you pass the name of the instance
variable to accessor . Here, you pass the symbol :name. When the program
runs, the call to class_eval automatically generates this code inside the
Element class:
def name
@name
end
def name=(val)
@name = val
end
The name method returns the current value of the instance variable
@name . The name= method accepts a value and assigns it to @name . Test
this by creating an instance of the Element class and trying to get and set
the value of name:
irb(main):001:0> e = Element.new "lead"
=> #
Advanced Ruby 101
irb(main):002:0> e.name = "gold"
=> "gold"
irb(main):003:0> puts e.name
gold
=> nil
First, create a new Element and initialize its name with "lead" . Next,
use the name= method to assign the new name "gold" . Finally, use the name
method to display the value of @name . There you have it. With a bit of
metaprogramming magic, you turned lead into gold.
method_missing
Whenever Ruby can’t find a method, it calls method_missing on the receiver.
This method receives the original method name as a symbol, an array of
arguments, and any block passed to the method call.
By default, method_missing calls super, which passes the method up the
ancestor chain until it finds an ancestor class containing the method. If the
method reaches the BasicObject class, it raises a NoMethodError exception. You
can override method_missing by defining your own implementation in a class
to intercept these method calls and add your own behavior.
Let’s start with a simple example so you can see how it works. This class
echoes any unknown method calls back to you three times:
class Echo
def method_missing(name, *args, &block)
word = name
puts "#{word}, #{word}, #{word}"
end
end
Now that method_missing is overridden, if you try to call a nonexistent
method on an instance of this class, you’ll just see that method’s “echo” in
the terminal:
irb(main):001:0> echo = Echo.new
=> #
irb(main):002:0> echo.hello
=> hello, hello, hello
A real-world use for method_missing is the Rails dynamic finder.
Using dynamic finders, you can write Active Record queries like
Post.find_by_title("First Post") instead of Post.where(title: "First
Post").first.
Dynamic finders can be implemented using method_missing. Let’s
define our own version of dynamic finders. Instead of method names
like find_by_attribute, we’ll use query_by_attribute so we can avoid conflicts with the built-in methods.
102 Chapter 7
Open the Post model at app/models/post.rb in your blog directory to
follow along with this example:
class Post < ActiveRecord::Base
validates :title, :presence => true
has_many :comments
def self.method_missing(name, *args, &block)
if name =~ /\Aquery_by_(.+)\z/
where($1 => args[0]).first
else
super
end
end
end
First, define the method_missing class method because our query
_by_attribute method will be called on the Post class. Next, test the name
against a regular expression .
Finally, call the built-in where method using the string captured by
the regular expression and the first argument passed to the method. Be
sure to call super if the string doesn’t match; this ensures that unknown
methods will be sent to the parent class.
The regular expression /\Aquery_by_(.+)\z/ matches strings that start with
“query_by_” and then captures the rest of the string using parenthesis. A full
discussion of regular expressions is beyond the scope of this book. The website
http://rubular.com/ is a great way to edit and test regular expressions online.
N OTE
The real dynamic finders also check to make sure the captured string
matches an attribute of the model. If you try to call our query_by_attribute
method with nonexistent column, it raises a SQLException.
irb(main):001:0> Post.query_by_title "First Post"
=> #
Our implementation of query_by_attribute has one more problem:
irb(main):002:0> Post.respond_to? :query_by_title
=> false
Because we’re overriding method_missing to call this method, Ruby doesn’t
know that the Post class can respond to it. To fix this, we need to also override the respond_to_missing? method in the Post model at app/models/post.rb.
class Post < ActiveRecord::Base
--snip--
def self.respond_to_missing?(name, include_all=false)
name.to_s.start_with?("query_by_") || super
Advanced Ruby 103
end
end
Instead of the regular expression used in method_missing, we just check if
the method name starts with "query_by_" . If it does, this method returns
true. Otherwise, super is called. Now restart the Rails console and try again:
irb(main):001:0> Post.respond_to? :query_by_title
=> true
With this change in place, respond_to? returns true as expected. Remember to always override respond_to_missing? when using method_missing. Other
wise, users of your class have no way of knowing which methods it accepts,
and the duck typing techniques covered earlier will fail.
Summary
If you write enough Ruby, then you will eventually see all of the techniques
covered in this chapter used in real-world programs. When that time comes,
you can be confident that you’ll understand what the code does, instead of
just assuming that metaprogramming is some kind of magic.
In the next chapter, you’ll start building a new Rails application from
scratch. Along the way I’ll cover some advanced data-modeling techniques
and you’ll learn even more about Active Record.
For now, try these exercises.
Exercises
1.
2.
3.
104 Chapter 7
The Rails framework makes extensive use of modules both as namespaces
and to add behavior to classes. Open a Rails console inside your blog
directory and look at the ancestors of Post. How many ancestors does it
have? Based on their names, can you tell what some of them do?
Update the define_method sample by adding a cannot_ feature! method.
This method should set the value corresponding to the correct key in
the @features hash to false.
Verify that class_eval created the instance methods you expected inside
the Element class by calling Element.instance_methods(false). Then reopen the
Element class and call accessor :symbol to add two more methods for an
instance variable named @symbol.
8
Adv a nc e d Ac t i v e R e c o rd
When building a new application, work out the data
model first. A data model is a description of the models
in your program, along with their attributes and associations. First, identify the models needed and the
relationships between them, and then create tables
for these models and test them in the Rails console.
Once the data models are working properly, building
the rest of the application is much easier.
Some people think of diagrams with boxes and arrows when they hear
the words data model. These diagrams are unnecessary if you understand
how the models relate without them. This chapter does include some basic
diagrams, however, to illustrate different associations. In each diagram, the
arrows point from the foreign key in a child model to the primary key in
the parent model.
Once the manager_id field has been added to the employees table, you can
define the associations in the Employee model:
class Employee < ActiveRecord::Base
has_many :subordinates, class_name: 'Employee',
foreign_key: 'manager_id'
belongs_to :manager, class_name: 'Employee'
end
First, you add a has_many association for subordinates. Because this association refers to the Employee model, and not a model named Subordinate, you
must specify class_name: 'Employee' . You must also specify the foreign key
name, in this case, manager_id . Finally, add the belongs_to association for
the manager. Again, you must explicitly state the model’s class name because
Rails can’t figure it out based on the association name .
With these associations in place, you can call the subordinates method to
get a list of a manager’s subordinates. You can also use the methods manager
and manager= to get and set an employee’s manager. Almost every employee
should have a manager_id, as shown in Table 8-1. If your manager_id is nil, then
you must be the boss!
Table 8-1: The employees Table
id
name
manager_id
1
Alice
NULL
2
Bob
1
Notice that the manager_id for Bob is 1. That means Alice is Bob’s manager. Alice’s manager_id is NULL, which is nil in Ruby. She’s the CEO of this
two-person company.
Many-to-Many Associations
Whereas a one-to-many association only involves two tables, a many-tomany association always involves a third table known as a join table. The join
table stores foreign keys for each side of the association. It belongs_to each
of the models in the association.
Rails provides two different ways to set up a many-to-many association.
has_and_belongs_to_many
If you’re using a join table strictly for the association and need no additional data, then use a has_and_belongs_to_many association. You still need to
create the join table, but you don’t need to define a model for it. The join
table must be named after the two models it joins.
Advanced Active Record 107
For example, authors write many books, and some books have multiple
authors. All of the data you need is stored in either the author or book
model, so you can create a has_and_belongs_to_many association between
authors and books, as in Figure 8-2.
authors
id
name
authors_books
author_id
book_id
books
id
name
Figure 8-2: has_and_belongs_to_many association
Figure 8-2 shows the Author and Book models with the join table between
them. Define the association between these models as shown here:
class Author < ActiveRecord::Base
has_and_belongs_to_many :books
end
An author might write many books, but a book can also have many
authors:
class Book < ActiveRecord::Base
has_and_belongs_to_many :authors
end
For this association to work, the join table between authors and books
must be named authors_books and must contain fields author_id and book_id.
Use the rails generate command to create an empty migration file:
$ bin/rails g migration CreateAuthorsBooks
invoke active_record
create
db/migrate/..._create_authors_books.rb
Then edit the migration file to remove the primary key and create the
two foreign keys:
class CreateAuthorsBooks < ActiveRecord::Migration
def change
create_table :authors_books, id: false do |t|
t.references :author, null: false, index: true
t.references :book, null: false, index: true
end
end
end
108 Chapter 8
The t.references :author statement indicates this field is a foreign
key that references an Author model. The field is named author_id. The
null: false option adds a constraint so NULL values are not allowed, and
the index: true option creates a database index to speed up queries on this
field. The next line creates the book_id field, also with a NULL constraint
and database index.
You can also use the create_join_table method inside the migration to
create the join table. This method takes the names of the associations and
creates the correct table with no primary key and a foreign key for each
association with a NULL constraint. This method does not automatically
create indices for the foreign keys. You can add indices as shown here:
class CreateAuthorsBooks < ActiveRecord::Migration
def change
create_join_table :authors, :books do |t|
t.index :author_id
t.index :book_id
end
end
end
After creating the join table, you don’t need to do anything to make the
association work. There is no model associated with the join table. With a
has_and_belongs_to_many association, Rails manages the join table for you.
has_many :through
If you would like to store additional information in the join table besides
the foreign keys of the associated models, use a has_many :through association. For example, you could model the association between bands and venues
using a join table named performances. Figure 8-3 shows the relationship
among bands, performances, and venues.
bands
id
name
performances
id
showtime
band_id
venue_id
venues
id
name
Figure 8-3: has_many :through association
Each performance belongs to a band and a venue. It also has a showtime. The models look like this:
class Band < ActiveRecord::Base
has_many :performances
has_many :venues, through: :performances
end
Advanced Active Record 109
A band performs many times, and so the band is associated with many
different venues through its performances:
class Venue < ActiveRecord::Base
has_many :performances
has_many :bands, through: :performances
end
A venue hosts many performances. The venue is associated with many
different bands through the performances it hosts:
class Performance < ActiveRecord::Base
belongs_to :band
belongs_to :venue
end
Performances associate a band with a venue. A venue can also store additional data, such as the showtime of the performance, in the performances
table.
Single-Table Inheritance
Sometimes you need to store a hierarchy of classes in the database. Most
relational databases don’t support inheritance, but you can use single-table
inheritance to create these models and store the inheritance structure in the
database.
For example, imagine you are writing an application to manage a pet
store. You need a way to model different types of pets such as dogs and fish.
Pet dogs and pet fish share many of the same attributes and methods, so it
makes sense for both of them to inherit from a parent class named Pet.
In Rails, you can create a single table for pets and then store records
for the two child classes Dog and Fish in the same table. Rails uses a column
named type to keep track of the type of object stored in each row. In addition to the columns needed by the parent model, you also need to add all
columns needed by the child models to the table. You need this because all
models are stored in the same table.
The parent model Pet is a normal Active Record model. The Pet model
inherits from ActiveRecord::Base:
class Pet < ActiveRecord::Base
end
The Dog model inherits from Pet:
class Dog < Pet
end
110 Chapter 8
The Fish model also inherits from Pet:
class Fish < Pet
end
With these models in place, you can store records of all three types in a
single table named pets, shown in Table 8-2.
Table 8-2: The pets Table
id
type
name
cost
1
Dog
Collie
200
2
Fish
Gold Fish
5
3
Dog
Cocker Spaniel
100
These three rows from the pets table hold data for the Dog and Fish
models. You can now make calls like Pet.count to count the pets in the table.
Calling Dog.count returns 2 and Fish.count returns 1. Because Rails knows
teach record type, pet = Pet.find(2) returns an object of type Fish.
You’ll look at another example of single-table inheritance in the next
section, when you create the post models for your new application.
Polymorphic Associations
With polymorphic associations, a model can belong to more than one other
model using a single association. The classic example of a polymorphic association is allowing comments on multiple types of objects. For example, you
might want to let people comment on both posts and images. Here is what
your comment model might look like using a polymorphic association:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Instead of using belongs_to :post or belongs_to :image, you specify that
a comment belongs_to something called :commentable. This name can be
anything you like, but the convention is to make it an adjective form of the
model name.
The comments table will need two fields for this association to work, an
integer field named commentable_id and a string field named commentable_type.
The commentable_type field holds the class name of the object that owns this
comment. This setup is similar to the type column in the single-table inheritance example you saw in the previous section. The commentable_id is a foreign
key referring to the id of the object that owns this comment.
Advanced Active Record 111
Include as: :commentable on the has_many :comments associations in models
that can have comments:
class Post < ActiveRecord::Base
has_many :comments, as: :commentable
end
class Image < ActiveRecord::Base
has_many :comments, as: :commentable
end
The has_many association works the same as always. A method call like
@post.comments returns a list of comments associated with the post. It works
by looking for comments that match both the id of the @post object and the
class name Post.
If your application grows and you need comments on other models, you
can add the same has_many association to the new model without changing
anything in the Comment model.
That’s enough theory for now. Let’s put some of this knowledge to work.
The Social Application
In this section, you’ll build the data model for a social networking service
similar to Tumblr. You need models for users and posts. You also need to
represent a user following another user as well as several different types of
posts, and users should be able to comment on posts.
Start by creating a new, empty Rails application in your code directory:
$ cd code
$ rails new social
$ cd social
I’m calling my application social, but call yours whatever you like. Who
knows, you may launch this app and sell it for a billion dollars someday!
Now let’s work through the models needed for this application.
User Model
If this is to be a social site, the first thing you need is a model for users and
the relationships between them. Tumblr, like Twitter, doesn’t use the idea of
friendship between users. Instead, you subscribe to another user’s updates
by “following” that user.
Start by creating a new resource named User. For now, add string
fields for name and email. You can always add more fields later by creating
another database migration. The following command creates a controller,
model, database migration, and other files for users:
$ bin/rails generate resource User name email
112 Chapter 8
Create the users table by running this new database migration:
$ bin/rake db:migrate
Next, you need to create a model to represent the idea of subscriptions.
A subscription is a type of self join, but it is a many-to-many association,
so you need a join table. What should this model contain? You subscribe
to another user’s posts by following them. You can call the user you are
following a leader. So you need to store a leader_id and a follower_id in the
subscriptions table.
When one user follows another user, the following user’s id is stored in
the follower_id field and the other user’s id is stored in the leader_id field.
This setup allows you to find a list of a user’s followers and leaders easily.
$ bin/rails g model Subscription leader:references follower:references
invoke active_record
create
db/migrate/..._create_subscriptions.rb
create
app/models/subscription.rb
invoke
test_unit
create
test/models/subscription_test.rb
create
test/fixtures/subscriptions.yml
Because this is a join table, use the model generator to create a database migration and model for subscriptions. Don’t forget to update your
database:
$ bin/rake db:migrate
Now that you’ve created the tables, you need to update the model files
to define the associations. First, open the file app/models/subscription.rb in
your editor:
class Subscription < ActiveRecord::Base
belongs_to :leader, class_name: 'User'
belongs_to :follower, class_name: 'User'
end
You used leader:references and follower:references when creating the
model, so the Rails model generator added two belongs_to associations to
the Subscription model for you. Both :leader and :follower actually refer to
a User, so you need to add the class name User. By default, Rails looks for
model names that match association names. If you don’t specify a class
name, Rails looks for models named Leader and Follower. Figure 8-4 shows
the tables for users and subscriptions.
Note
In reality, these tables also include created_at and updated_at timestamps, but I left
these out of the diagrams in this chapter for brevity.
Advanced Active Record 113
users
id
name
email
subscriptions
id
leader_id
follower_id
Figure 8-4: Subscription associations
In the subscriptions table, both leader_id and follower_id are foreign keys
referring to a user. Now that the Subscription associations are done, let’s add
the User associations. Open the file app/models/user.rb in your editor:
class User < ActiveRecord::Base
has_many :subscriptions, foreign_key: :follower_id,
dependent: :destroy
has_many :leaders, through: :subscriptions
end
Start with the fact that a user has many subscriptions. In this case, you
need to specify the foreign key to use. Normally, you would call this user_id,
but you’re modeling leaders and followers, so call it follower_id instead .
Also specify what happens if this user is deleted with dependent: :destroy .
This tells Rails to destroy any associated subscriptions if this user is ever
destroyed. Finally, add the has_many:through association to leaders .
Next, add a few methods to the model to make working with the associations easier. You can also use these methods to test the associations in
the Rails console:
class User < ActiveRecord::Base
has_many :subscriptions, foreign_key: :follower_id,
dependent: :destroy
has_many :leaders, through: :subscriptions
def following?(leader)
leaders.include? leader
end
def follow!(leader)
if leader != self && !following?(leader)
leaders << leader
end
end
end
First, add a predicate method, a method returning a true or false value,
called following? to see if the current user is following another user. This
method checks to see if the current user’s leaders collection includes the
leader passed as an argument to the method.
114 Chapter 8
Then, add the follow! method to indicate that the current user is
following another user. This method ensures the current user isn’t trying
to follow himself or herself and isn’t already following the other user . If
neither case is true, the leader passed to this method is inserted into the
current user’s leaders collection with <<, the insertion operator.
With these methods in place, you can now launch a Rails console and
test your associations:
$ bin/rails console
Start by creating two users:
irb(main):001:0> alice = User.create name: "Alice"
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "users" ...
(0.8ms) commit transaction
=> #
irb(main):002:0> bob = User.create name: "Bob"
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "users" ...
(0.8ms) commit transaction
=> #
Now, call the follow! method on alice and pass in bob. Then call the following? method on alice to confirm that follow worked correctly. Finally, call
following? again to see if bob is following alice:
irb(main):003:0> alice.follow! bob
User Exists (0.2ms) SELECT ...
(0.1ms) begin transaction
SQL (16.1ms) INSERT INTO ...
(20.4ms) commit transaction
User Load (0.3ms) SELECT ...
=> #
irb(main):004:0> alice.following? bob
=> true
irb(main):005:0> bob.following? alice
User Exists (0.2ms) SELECT ...
=> false
The call to alice.follow! bob adds bob to collection of leaders for alice.
Next, the call to alice.following? bob checks to see if the alice.leaders collection includes bob. It does, so the method returns true. Of course, it doesn’t
actually look for bob, but the id of the User referred to as bob. The call to
bob.following? alice returns false. The bob.leaders collection is empty, so
bob is not following alice. Tables 8-3 and 8-4 show the users and subscriptions
tables after Alice follows Bob, again with the timestamp fields omitted.
Advanced Active Record 115
Table 8-3: The users Table
id
name
email
1
Alice
NULL
2
Bob
NULL
The users table holds records for alice and bob.
Table 8-4: The subscriptions Table
id
leader_id
follower_id
1
2
1
The subscriptions table holds a single record representing the asso
ciation between alice and bob. The leader_id is 2, the id of bob; and the
follower_id is 1, the id of alice. This means alice is following bob.
At this point, you can get a list of every user that alice is following by
calling the leaders method. Having this list is helpful, but it’s only half of
what you need. You also want to be able to list a user’s followers. To do
this, use the subscriptions table again, only this time going in the opposite
direction.
You need another has_many association on the Subscription model that is
the reverse of the existing association. You can then use that association to
find followers.
class User < ActiveRecord::Base
has_many :subscriptions, foreign_key: :follower_id,
dependent: :destroy
has_many :leaders, through: :subscriptions
has_many :reverse_subscriptions, foreign_key: :leader_id,
class_name: 'Subscription',
dependent: :destroy
has_many :followers, through: :reverse_subscriptions
def following?(leader)
leaders.include? leader
end
def follow!(leader)
if leader != self && !following?(leader)
leaders << leader
end
end
end
This association is the reverse of the existing :subscriptions association.
There’s no clever word for the reverse of a subscription, so name the association :reverse_subscriptions. This association uses the leader_id field as the
foreign key . Because the association name doesn’t match the name of
116 Chapter 8
the model, you also need to specify a class name . As with the subscription association, also specify dependent: :destroy so you aren’t left with
orphan records in the subscriptions table if a user is destroyed. After adding the :reverse_subscriptions association, you can use it to add another
has_many :through association for :followers .
Restart the Rails console for these changes to take effect, and then try
the new association:
irb(main):001:0> alice = User.find(1)
User Load (0.3ms) SELECT ...
=> #
irb(main):002:0> bob = User.find(2)
User Load (0.3ms) SELECT ...
=> #
irb(main):003:0> alice.followers
User Load (0.2ms) SELECT ...
=> #
irb(main):004:0> alice.followers.to_a
=> []
irb(main):005:0> bob.followers.to_a
User Load (0.2ms) SELECT ...
=> [#]
Because you restarted the console, you first need to find your
users in the database . Call the followers method on alice to see if
she has any followers . This method returns a type of relation called
an ActiveRecord::Associations::CollectionProxy. I made the output a little
easier to read by chaining to_a after followers, which converts the output
to an array .
The output shows that alice has no followers and bob has a single follower—alice. The User associations and methods are working correctly so
far. Now that users can follow each other, let’s move on to posts.
Post Models
People don’t just want to share plain text on a social network—they also
want to share images, links, and videos. We should allow our users to create a different kind of post for each type of content, though the post types
will share some common functionality. This sounds like a perfect use for
inheritance.
First, create a base model called Post, and then inherit from that class
to create models for TextPost, ImagePost, and so on. You can use singletable inheritance to create these models and store the inheritance structure in the database. Because the posts table holds records for all types
of posts, you must add columns needed by the other models to the posts
table. In addition to the usual title and body fields, add a url field to store
the address of an image for image posts and a type field for single-table
inheritance.
Advanced Active Record 117
With those requirements in mind, generate the post resource and
update your application’s database:
$ bin/rails g resource Post title body:text url type user:references
$ bin/rake db:migrate
The user:references option adds a user_id field so you can associate posts
with users. Don’t forget to update your application’s database.
Now you’re ready to create resources for the different types of posts.
$ bin/rails g resource TextPost --parent=Post --migration=false
$ bin/rails g resource ImagePost --parent=Post --migration=false
Here, I’ve passed two options to the resource generator. The
--parent=Post option indicates that these models inherit from Post and
the --migration=false option tells the generator to not create a database
migration for this resource. A database migration is not needed because
these resources are stored in the posts table you created earlier.
First, let’s update the newly created Post model in app/models/post.rb
to make sure all posts have an associated user and type:
class Post < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
validates :type, presence: true
end
All posts in our social application belong to an individual user. This
validation ensures that a Post can’t be created without an associated user_id .
The type validation validates that all records are identified as either a
TextPost or an ImagePost.
Now add validations to the TextPost and ImagePost models. First, edit
app/models/image_post.rb and add a URL validation to the ImagePost model:
class ImagePost < Post
validates :url, presence: true
end
The url field holds the address of the image for an ImagePost. Users can
copy a URL from an image sharing site such as Flickr or Imgur. The application shouldn’t allow an ImagePost to be saved without an image url.
Then update the TextPost model in app/models/text_post.rb to check for a
post body:
class TextPost < Post
validates :body, presence: true
end
118 Chapter 8
The application also shouldn’t allow a TextPost to be saved without
body text.
While you’re editing models, also add the associations for the new
post models under the rest of the has_many associations to the User model
at app/models/user.rb:
class User < ActiveRecord::Base
has_many :subscriptions, foreign_key: :follower_id,
dependent: :destroy
has_many :leaders, :through => :subscriptions
has_many :reverse_subscriptions, foreign_key: :leader_id,
class_name: 'Subscription',
dependent: :destroy
has_many :followers, through: :reverse_subscriptions
has_many :posts, dependent: :destroy
has_many :text_posts, dependent: :destroy
has_many :image_posts, dependent: :destroy
--snip--
Now you can restart the Rails console and use these new models:
irb(main):001:0> alice = User.find(1)
User Load (42.0ms) SELECT ...
=> #
irb(main):002:0> post1 = alice.text_posts.create(body: "First Post")
(0.1ms) begin transaction
SQL (0.7ms) INSERT INTO ...
(1.9ms) commit transaction
=> #
irb(main):003:0> post2 = alice.image_posts.create(
url: "http://i.imgur.com/Y7syDEa.jpg")
(0.1ms) begin transaction
SQL (0.7ms) INSERT INTO ...
(1.9ms) commit transaction
=> #
irb(main):004:0> alice.posts.to_a
Post Load (32.3ms) SELECT ...
=> [#, #]
irb(main):005:0> alice.text_posts.to_a
TextPost Load (0.4ms) SELECT ...
=> [#]
Because you restarted the console, first find the User representing
alice . Then create a TextPost and an ImagePost belonging to alice. The
posts method on the User model returns all posts associated with that user
regardless of type . Note that the TextPost and ImagePost you just created
are both returned in the same collection. The text_posts method returns
only TextPost objects .
Advanced Active Record 119
Comment Model
Now that the models for users and posts are in place, create the comments
model for the application. Add a text field to hold the body of the comment,
a post_id to reference the post that owns this comment, and a user_id to reference the user who left the comment.
Note that I am not using a polymorphic association with these comments.
Because my different post types all inherit from the base class Post, I can
simply associate Comment with Post, allowing comments on any type of post.
$ bin/rails g resource Comment body:text post:references user:references
$ bin/rake db:migrate
Also add has_many :comments to the User and Post model to complete the
associations among users, posts, and comments. Figure 8-5 shows the tables
you created in this chapter and their associations.
subscriptions
id
leader_id
follower_id
users
id
name
email
posts
id
title
body
url
type
user_id
comments
id
body
post_id
user_id
Figure 8-5: The social application data model, with timestamps omitted
With this, you have all of your models and are well on your way to building your new social network.
Summary
I covered some pretty advanced database modeling techniques in this chapter. The User model has several complex associations. The different types
of posts demonstrate single-table inheritance. Luckily, the Comment model
didn’t contain any surprises.
In the next chapter, I’ll talk about authentication, and you’ll start adding controller actions and views so users can sign up and log in to your
social network.
120 Chapter 8
Exercises
1.
2.
3.
You specified dependent: :destroy on all has_many associations in this
chapter to ensure that dependent models would be removed. For
example, because the Post model has a dependent: :destroy association
with the User model, if a User is destroyed, then all of the user’s posts
are also destroyed. What do you think would happen if you specified
dependent: :destroy on a belongs_to association?
Add validations to the Comment model to ensure that every comment
belongs to a User and a Post. Your application shouldn’t allow a Comment
to be created without a user_id and post_id. You should also ensure that
all comments have text in the body field.
Use the Rails console to create a new User. Create a TextPost or ImagePost
belonging to this User and at least one Comment. Now destroy the User, and
make sure the associated Post and Comment are also destroyed.
Advanced Active Record 121
9
Au t he n t ic at ion
Identity is a core concept in any social network, and
authentication is the act of identifying yourself to a
system. You want users to be able to sign up for new
accounts and log into your application. Although
gems like devise and authlogic provide complete
authentication systems for Rails applications, in this
chapter, you’ll get your hands dirty by building your
own system instead.
In addition to the signup, login, and logout actions, you’ll also add
methods for getting the current logged-in user’s identity and redirecting
anonymous users to the login page. This authentication system will require
controllers and views, so before starting, let’s take a moment to add a little
style to your site with the Bootstrap framework.
The Authentication System
The purpose of the authentication system is to identify the current user
and only display pages the user wants to see or is authorized to see. You’ll
use a combination of an email address and password to identify users.
Email addresses are a good choice because they are globally unique. No
two people have the same email address.
In your application, anonymous users are only allowed to see pages
for logging in or signing up for a new account. Every other page should be
restricted.
Post Index and Show
Before you start building the authentication system, you need data to protect from anonymous users. Let’s add the index and show pages for the Post
models created in the last chapter. First, you need to add controller actions.
Open the file app/controllers/posts_controller.rb in your editor and add these
index and show methods:
class PostsController < ApplicationController
def index
@posts = Post.all
end
def show
@post = Post.find(params[:id])
end
end
These two actions are similar to the index and show actions in the blog
from Chapter 4. The index action retrieves all posts from the database
and assigns them to the @posts variable. It then renders the view at app/
views/posts/index.html.erb. The show action finds the requested post using
the id from the params hash, assigns it to @post, and renders the view at app/
views/posts/show.html.erb.
Now you need to create corresponding view templates for these actions.
Create a new file named app/views/posts/index.html.erb and add the following
code:
Home
<%= render @posts %>
The index view adds a header using the Bootstrap page-header class
and renders the collection @posts using partials.
Because you’re using partials to render the posts, add those next; you’ll
need a partial for each post type—of which there are two—so you need two
partial files.
Authentication 125
First, create the file app/views/text_posts/_text_post.html.erb and open it
for editing:
<%= text_post.title %>
By <%= text_post.user.name %>
<%= text_post.body %>
This partial uses Bootstrap’s panel component to display a TextPost. The
panel class adds a gray border around the content. The panel-heading class
adds a light gray background. The title is then rendered inside an h3 element with <%= text_post.title %> . The panel-body class adds padding to
match the heading. The post author and body are rendered in this section.
Then create the file app/views/image_posts/_image_post.html.erb with
the following content. The ImagePost partial is just a slight variation on the
TextPost partial:
<%= image_post.title %>
By <%= image_post.user.name %>
<%= image_tag image_post.url, class: "img-responsive" %>
<%= image_post.body %>
This partial uses the ERB image_tag helper to add an image tag with the
source set to image_post.url , the location of the image. This line also adds
Bootstrap’s img-responsive class to the image, which causes it to scale automatically based on the browser width.
With these views in place, start the Rails server and look at the
application:
$ bin/rails server
126 Chapter 9
Now go to http://localhost:3000/posts in your web browser. The Post index
view should look similar to Figure 9-1, depending on how many posts you
created in the Rails console.
Figure 9-1: The Post index view
You created two posts in the previous chapter, and your application’s
Post index view currently shows those two posts. You didn’t add titles in the
last chapter, so the headings are blank.
Now that the Post partials have been created, the Post show view can also
use those partials. Create the new file app/views/posts/show.html.erb with the
following content:
Post
<%= render @post %>
<%= link_to "Home", posts_path,
class: "btn btn-default" %>
The show view is similar to the index view with two exceptions. It renders
a single post instead of a collection of posts, and it includes a button
that links back to the posts index page.
Go to http://localhost:3000/posts/1 to see it in action, as in Figure 9-2.
Authentication 127
Figure 9-2: The Post show view
Now that the application has actions and views for displaying posts, let’s
move on to adding authentication to protect these actions from anonymous
users.
Sign Up
Here, you’ll implement a user sign-up process that asks for an email address,
password, and password confirmation. If the user enters an email address
that isn’t already in the database and provides passwords that match, the
system will create a new User and thank the user for signing up.
You can already store the new user’s email address because you have a
string field named email in the users table. You need to be more careful, however, with passwords. Never store a user’s password in plain text. Instead, store
a hashed version of the password, known as a password digest. The secure password feature in Rails provides built-in support for password hashing, using a
hashing algorithm called bcrypt. Bcrypt is a secure one-way hash.
You can enable the secure password feature by calling the method
has_secure_password in a Rails model. This method adds the password and
password_confirmation attributes to the model and expects the model to
have a string field named password_digest. It adds validations that require
matching password and password_confirmation attributes on creation. If these
attributes match, it automatically hashes the password and stores it in the
password_digest field.
First, edit your application’s Gemfile and add the bcrypt gem. Because
many applications include an authentication system, a commented-out line
is already available for this gem. Remove the hash mark at the beginning of
that line and save the file.
gem 'bcrypt', '~> 3.1.7'
Anytime you change the Gemfile, you also need to run the bin/bundle
install command to update the gems installed on your system:
$ bin/bundle install
128 Chapter 9
The next step is to add the password_digest field to the users table and
run the database migration with bin/rake db:migrate so you can store the
user’s hashed password:
$ bin/rails g migration AddPasswordDigistToUsers password_digest
Now you need to turn on the secure password feature for the User
model. Open app/models/user.rb and add the line has_secure_password below
the has_many associations you added in the last chapter. While you’re editing
that file, also add presence and uniqueness validations for the email field:
class User < ActiveRecord::Base
--snip-has_secure_password
validates :email, presence: true, uniqueness: true
--snip-end
The default route for creating a new user is http://localhost:3001/users/
new. That works, but a custom route such as http://localhost:3001/signup
might be easier to remember.
Edit config/routes.rb and add a route for the sign-up page. After a user
signs up for an account or logs in to your application, you want to redirect
the user to the home page. So set the root route to the posts index page
while you’re editing this file.
Rails.application.routes.draw do
resources :comments
resources :image_posts
resources :text_posts
resources :posts
resources :users
get 'signup', to: 'users#new', as: 'signup'
root 'posts#index'
end
Open app/controllers/users_controller.rb and add the necessary actions to
UsersController for creating new Users:
class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
Authentication 129
redirect_to root_url,
notice: "Welcome to the site!"
else
render "new"
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
The new method instantiates an empty new User object and renders
the sign-up form. The create method instantiates a User object using the
parameters passed from the form. Then, if the user can be saved, it redirects
the user to the root of the site and displays a welcome message. Otherwise,
it renders the new user form again.
Now that the controller actions are in place, add the sign-up form in
app/views/users/new.html.erb:
Sign Up
130 Chapter 9
<%= form_for(@user) do |f| %>
<% if @user.errors.any? %>
<%= pluralize(@user.errors.count, "error") %>
prevented you from signing up:
<% @user.errors.full_messages.each do |msg| %>
- <%= msg %>
<% end %>
<% end %>
<%= f.label :email %>
<%= f.email_field :email, class: "form-control" %>
<%= f.label :password %>
<%= f.password_field :password, class: "form-control" %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation,
class: "form-control" %>
<%= f.submit class: "btn btn-primary" %>
<% end %>
The first half of this form displays error messages , if any. The form
uses a div with the Bootstrap class form-group to group labels and inputs ,
and adds the class form-control to input controls . Bootstrap uses these
classes to apply styles to the form.
Go to http://localhost:3000/signup in your web browser to see the sign-up
form, as in Figure 9-3.
Figure 9-3: The sign-up form
In the create action, you added a flash message to welcome new users, but
your views don’t have a place for displaying flash messages yet. Bootstrap
includes an alert class that’s perfect for displaying flash messages. Open the
application layout at app/views/layouts/application.html.erb and add a section
for flash messages, as shown here:
--snip-
<% if notice %>
<%= notice %>
<% end %>
<% if alert %>
<%= alert %>
<% end %>
<%= yield %>