You are on page 1of 153

Rails 101

xdite
This book is for sale at http://leanpub.com/rails-101
This version was published on 2014-06-10

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
2013 - 2014 xdite

Also By xdite
Maintainable Rails View
Lean SaaS
Land Dream Rails Job

Rails 4.0.0 + Ruby 2.0.0

Contents
CHANGELOG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Logdown . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2
2

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Rails . .
Git . . . . . . . . . .
. . . . . . . .
Linux Command Line

.
.
.
.

5
5
6
6

Ruby on Rails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7
8

0 - Hello World .
. . . . . . . .
Hello World .
Rails Routing . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

13
13
19
22

1 - Group - CRUD RESTful


Ch 1.0 CRUD . . . . . . . . . . . . . . . . .
Ch 1.0 CRUD Scaffold .
Ch 1.0 Bootstrappers
Ch 1.1 Group . . . . . . . . . . . . . .
Ch 1.1 () RESTful . . . . . . . . . . . .
Ch 1.1.1 Groups Controller index
Ch 1.1.2 Groups Controller show
Ch 1.1.3 Groups Controller new .
Ch 1.1.4 Groups Controller create
Ch 1.1.4 () Strong Parameters . . . . . .
Ch 1.1.5 Groups Controller edit .
Ch 1.1.6 Groups Controller update

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

24
25
26
30
31
33
36
39
41
43
45
48
50

CONTENTS

Ch 1.1.7 Groups Controller destroy . . . . . . . . . . . . . . . . . . . . . . . .


RESTful on Rails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
More about RESTful on Rails . . . . . . . . . . . . . . . . . . . . . . . . . . .

51
53
60

2 - Group - RESTFul . . . .
Ch 2.1 Post . . . . . . . . . . . . . . . . . . . . . . . .
Ch 2.1.1 Groups controller show Post
Ch 2.1.2 Posts Controller new . . . . . . . . . . .
Ch 2.1.3 Posts Controller create . . . . . . . . . .
Ch 2.1.4 Posts Controller edit . . . . . . . . . . .
Ch 2.1.5 Posts Controller update . . . . . . . . . .
Ch 2.1.6 Posts Controller destroy . . . . . . . . .
Ch 2.1.7 before_action . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

63
64
67
69
70
72
73
74
75

3 - Group Post . .
Ch 3.0 devise Rails 4 . . . . . . . . . . . . . . .
Ch 3.1 Action
Ch 3.2 Group User . . . . . . .
Ch 3.3 Post User . . . . . . . .

78
79
83
84
91

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

4 - User . . . . . . . . . .
Ch 4.1
Ch 4.1.2 model method after create . . . . . . . . . . .
Ch 4.1.3 join quit action . . . . . . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

. 96
. 98
. 101
. 103

5 - Account . . . . . . . . . . . .
Ch 5.1 User Group
Ch 5.2 User .
Ch 5.3 DESC . . . . . . .
Ch 5.4 Group DESC . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

105
106
108
111
112

6 - Refactor code . . . . . . .
Ch 6.1 helper code . .
Ch 6.2 helper html
Ch 6.3 partial html . . . .
Ch 6.4 scope query . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

115
116
117
119
121

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

7 - Rake db:seed . . . . . . . . . . . . . . . . . . . . . . . . . . 123


Ch 7.1 Rake . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
8 - deploy VPS . .
Ch 8.1 Rails Production
Ch 8.2 Capistrano . . . . . . . . . . . . . . .
Ch 8.3 Capistrano . . . . . . . . .
Ch 8.4 Deploy with Rails 4 . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

126
127
128
131
132

CONTENTS

Asset Pipeline .
SCSS . . . . . . . . . . . .
CoffeeScript . . . . . . . .
Asset Pipeline . .
Rails 4 with Asset Pipeline
. . . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

133
133
136
138
140
141

. . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
Ruby on Rails . . . . . . . . . . .
Testing . . . . . . . . . . . . . . .
. . . . . . . . . . .
Ruby / Rails code . . . . . . . . .
Ruby code . . . . . . .
Object-oriend Design in Ruby on Rails
Rails . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

142
142
143
143
143
144
144
145
145

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Resources of latest Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146

CHANGELOG
2014/01
Fix

2013/12
Fix
Upgrade Bootstrappers
Fix Bootstrappers
mysql SQLite
Fix production hack
TODO
RESTFul
Recap
Facebook https://www.facebook.com/groups/rails101/
2013/11
Bootstrapper, Rails 4.0.0 , Repo
Helper
Partial
Scope
Capistrano
Asset Pipeline
Rails 4.0.0 production hack
2013/06 Ch6


xdite Ruby on Rails 6 ( Since 2007 )
Web blog Blog.XDite.net Rails

Ruby Taiwan ConfRuby China ConfReddot Conf ( Singapore Ruby Conf )


Rails
Ruby on Rails Facebook World Hack 2013 Global Grand Prize
Rocodev

Logdown
Logdown Rocodev Blog
rails101 coupon USD $10 Logdown

sdlong
http://blog.xdite.net
http://blog.xdite.net
http://paperclip.io/pages/about
http://rocodev.com
http://logdown.com
http://rocodev.com


1.
2.
3.
4.
5.
6.
7.
8.

https://www.facebook.com/groups/rails101/ FB
Google Stack Overflow
@
migration

touch tmp/restart.txt
http://guides.rubyonrails.org
development.log Chrome DevTools Console


2009 Rails Developer
Rails
Developer
Rails Rails /

build
Rails

host Rails Meetup http://www.


meetup.com/taipei-rails-meetup/

Facebook https://www.facebook.com/groups/rails101/

Gist

https://github.com/xdite/groupme

Rails Rails

MacOS 10.7, 10.8, 10.9 command line


Debian 5,6 / Ubunbtu 10,11,12 apt-get lib
Ruby 1.8.7 -> 1.9.3 -> 2.0.0 library
Rails 3.0 -> 3.1 -> 3.2 4.0.0 rc1, beta1, official deploy

Stackoverflow

http://gist.github.com
http://stackoverflow.com/

Rails

Rails Developer Rails

Git
Vim SublimeText2
Linux Command Line

Git
Codeshool Git Rails
Git
TryGit http://www.codeschool.com/courses/try-git
GitReal http://www.codeschool.com/courses/git-real
GitReal2 http://www.codeschool.com/courses/git-real-2

1. Github
2. Git

https://github.com

git commit
git push
git pull
git branch
git checkout
git merge

Rails

Vim
c9s Vim Hacks vim

Sublimtext 2
Sunlimtext 2 : http://www.sublimetext.com/2
Sublime Text SublimeText

Linux Command Line


PeepCode Meet the Command Line
PeepCode Advanced Command Line

Meet the Command Line


Advanced Command Line

http://c9s.blogspot.com/2009/08/vim-hacks-coscup.html
https://www.facebook.com/SublimeTextTW
http://docs.sublimetext.tw/
http://peepcode.com/products/meet-the-command-line
http://peepcode.com/products/advanced-command-line
http://peepcode.com/products/meet-the-command-line
http://peepcode.com/products/advanced-command-line

Ruby on Rails

Bug Free Rails

Rails Rails
lib gem
OSX 10.8MySQLImageMagickreadline .
project

Rocodev Ruby on Rails Bug Free

https://github.com/rocodev/guides/wiki/setup-mac-development

Mac Ruby on Rails


Rails

Rails Developer
Rails Developer MacBook + Best Practices /
brewLivereload Mac OS Gem
Rails
Linux package system maintainer Rails Developer

Framework

Rails Mac

http://rocodev.com

Ruby on Rails

1. Mac 10.9
2. Apple Store Xcode 5.0.4 Command Tools )

1
2
3

Homebrew

$ ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"


$ brew install git
$ brew update

4
5
6

$ brew tap homebrew/dupes


$ brew install apple-gcc42

XQuartz

ImageMagick X11 supportOSX 10.8


http://xquartz.macosforge.org/landing

Ruby on Rails

ImageMagick / MySQL

$ brew install imagemagick

1
2
3
4
5
6
7
8
9
10

Imagemagick

MySQL

$ brew install mysql


$ unset TMPDIR
$ mysql_install_db --verbose --user=`whoami` --basedir="$(brew --prefix mysql)" -\
-datadir=/usr/local/var/mysql -tmpdir=/tmp
$ mysql.server start
$ mysqladmin -u root password '123456'
$ mkdir -p ~/Library/LaunchAgents
$ find /usr/local/Cellar/mysql/ -name "homebrew.mxcl.mysql.plist" -exec cp {} ~/L\
ibrary/LaunchAgents/ \;
$ launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist

MySQL Mac OS X ver. 10.6 (x86, 64-bit), DMG


Archive MySQL

http://dev.mysql.com/downloads/mysql/

Ruby on Rails

10

RVM Ruby 2.0

Rails Ruby getsemt


Ruby Version Manager Ruby
Ruby bug
RVM

1
2
3
4

$ bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binsc\


ripts/rvm-installer)
$ . ~/.profile
$ source ~/.profile

1
2
3
4
5
6

RVM

Ruby 2.0

$ brew install libyaml


$ rvm pkg install openssl
$ rvm install 2.0.0 \
--with-openssl-dir=$HOME/.rvm/usr \
--verify-downloads 1
$ rvm use 2.0.0

RVM gem passenger-install-apache2-module sudo ,


sudo RVM ruby , .)

1
2
3
4

$
$
$
$

gem
gem
gem
gem

Ruby gems

install
install
install
install

rails --version 4.0.0


mysql2
capistrano
capistrano-ext

https://rvm.beginrescueend.com/

Ruby on Rails

11

HTTP Server ( Pow)

Pow HTTP Server

Pow 37 Signals open-source Rack Server Zero Config


Pow routing Pow project /etc/hosts
Pow rack-based rack framework
Passenger Mac apache solution
Installation
Pow
1

$ curl get.pow.cx | sh

Setting
Pow /.pow
project Pow wiki
1
2

$ cd ~/.pow/
$ ln -s ~/projects/wiki

http://wiki.dev http://localhost:3000/

project/wiki powder link

Powder Pow

Powder
Pow

http://pow.cx
http://37signals.com/
http://rack.rubyforge.org/
http://www.modrails.com/
http://pow.cx
http://pow.cx
https://github.com/Rodreegez/powder

Ruby on Rails
1

$ gem install powder

powder restart powder log

Ruby Pow Ruby


gemset RVM Ruby Pow
project .rvmrc

.rvmrc
1

rvm 2.0.0

12

0 - Hello World

Rails

Rails Hello World


Bundler
Pow
Rails

http://gembundler.com/
http://pow.cx
http://ihower.tw/rails3/environment-and-bundler.html

0 - Hello World

Rails 4.0.0
1

gem install rails --version 4.0.0

Terminal /projects groupmy Rails


1

rails new groupmy

groupmy
1

$ cd groupmy

rails new groupmy

14

0 - Hello World

15

Bundler
Bundler
Bundler Ruby

Bundler
Bundler Gemfile Rails Gemfile

source 'https://rubygems.org'

2
3
4

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'


gem 'rails', '4.0.0'

5
6
7

# Use sqlite3 as the database for Active Record


gem 'sqlite3'

8
9
10

# Use SCSS for stylesheets


gem 'sass-rails', '~> 4.0.0'

11
12
13

# Use Uglifier as compressor for JavaScript assets


gem 'uglifier', '>= 1.3.0'

14
15
16

# Use CoffeeScript for .js.coffee assets and views


gem 'coffee-rails', '~> 4.0.0'

17
18
19

# See https://github.com/sstephenson/execjs#readme for more supported runtimes


# gem 'therubyracer', platforms: :ruby

20
21
22

# Use jquery as the JavaScript library


gem 'jquery-rails'

23
24
25
26

# Turbolinks makes following links in your web application faster. Read more: htt\
ps://github.com/rails/turbolinks
gem 'turbolinks'

27
28
29

# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder


gem 'jbuilder', '~> 1.2'

30

http://gembundler.com/
http://gembundler.com/

0 - Hello World
31
32
33
34

16

group :doc do
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', require: false
end

35
36
37

# Use ActiveModel has_secure_password


# gem 'bcrypt-ruby', '~> 3.0.0'

38
39
40

# Use unicorn as the app server


# gem 'unicorn'

41
42
43

# Use Capistrano for deployment


# gem 'capistrano', group: :development

44
45
46

# Use debugger
# gem 'debugger', group: [:development, :test]

Bundler

bundle check :
bundle install :

bundle install

0 - Hello World

17

Pow
Rails Webserver rails s
port 3000 http://localhost:3000
Pow
Pow Pow http://groupmy.
dev rails s port 3000
rails s Ctrl-C

Pow touch tmp/restart.txt webserver

Server config/routes.rb
config/enviroments/*.rb config/database.yml Server
Ctrl-C rails s

Pow .rvmrc .powrc


powder gem Pow powder gem install powder
powder install Pow
powder link groupmy/
powder link http://groupmy.dev/ groupmy/
http://groupmy.dev/
.rvmrc
rvm 2.0.0

.powrc
1
2
3
4

if [ -f "$rvm_path/scripts/rvm" ] && [ -f ".rvmrc" ]; then


source "$rvm_path/scripts/rvm"
source ".rvmrc"
fi

powder open
http://pow.cx

0 - Hello World

18

rvm reload
bundle install
powder restart
touch tmp/restart.txt

development.log

rails s

Live Log Debug Pow Log debug


tail -f log/development.log Log

0 - Hello World

19

Hello World

http://groupmy.dev/

Rails
Hello World
Hello World pages controller wecleom action

0 - Hello World

1
2
3
4
5
6
7
8
9
10

1: pages controller

[~/projects/groupmy] (master) $ rails g controller pages


create app/controllers/pages_controller.rb
invoke erb
create
app/views/pages
invoke test_unit
create
test/functional/pages_controller_test.rb
invoke helper
create
app/helpers/pages_helper.rb
invoke
test_unit
create
test/unit/helpers/pages_helper_test.rb

2: welcome action

app/controllers/pages_controller.rb
1
2
3
4

class PagesController < ApplicationController


def welcome
end
end

3: welcome HTML view

app/views/pages/welcome.html.erb
1

Hello World!

4:

config/routes.rb root pages#welcome


1

root :to => "pages#welcome"

Hello World!

20

21

0 - Hello World

Hello world

0 - Hello World

Rails Routing
Rails config/routes.rb mapping
routing
1

get "subscriptions/new"

2
3
4
5

resources :posts do
resources :comments
end

6
7
8
9

namespace :admin do
resources :posts
end

10
11
12

match '/search' => "search#index", :as => "search"


root :to => "pages#welcome"

22

23

0 - Hello World

Ruby on Rails

1 - Group - CRUD RESTful

Group
Group


Group Post model CRUD
http://groupme.dev/group/1/post/2

CRUD action controller view


migration

Route
resources resources)
Rails RESTful
before_action

1 - Group - CRUD RESTful

25

Ch 1.0 CRUD
CRUD Create()Read()Update()Destroy()

Rails RESTful
Convention CRUD-like

Group Post Model


Group title, description
Post content
Group CRUD controller View
Posts CRUD controller View
routing Group Posts resources

1 - Group - CRUD RESTful

26

Ch 1.0 CRUD Scaffold


Rails powerful scaffold

Rails rails g scaffold [MODEL] model controller


CRUD action view

scaffold Groups Posts


1.1
Group name description
group scaffold
1

$ rails g scaffold group title:string description:text

Post content
post scaffold
1

$ rails g scaffold post content:text

db:migrate
1

$ rake db:migrate

[~/projects/groupmy] (master) $ rake db:migrate


(in /Users/xdite/projects/groupmy)

2
3
4
5
6
7

== CreateGroups: migrating ===================================================


-- create_table(:groups)
-> 0.0013s
== CreateGroups: migrated (0.0014s) ==========================================

8
9
10
11
12

== CreatePosts: migrating ====================================================


-- create_table(:posts)
-> 0.0012s
== CreatePosts: migrated (0.0013s) ===========================================

1 - Group - CRUD RESTful

http://groupme.dev/groups

27

1 - Group - CRUD RESTful

http://groupme.dev/posts/new

28

1 - Group - CRUD RESTful

29

Scaffold
groups post model migration
groups posts CRUD controller / action / view
config/routes resources :groups resources :posts

What is db migration?
web phpmyadmin db
Rails db migration
phpmyadmin
db migration project
db schema
db db
automation db schema db
migration file

group post CRUD

scaffold CRUD convention

CRUD Rails
CRUD scaffold

1 - Group - CRUD RESTful

30

Ch 1.0 Bootstrappers
Bootstrappers 2012
Boostrap Theme Gem Rails Best Practice

bootstrappers

gem install bootstrappers -v=4.2.1

bootstrappers

bootstrappers

bootstrappers groupme

Bootstrappers Chapter 1.1 groupme.dev


Pow Bootstrappers

https://github.com/xdite/bootstrappers
http://twitter.github.io/bootstrap/

1 - Group - CRUD RESTful

Ch 1.1 Group

Group model

rails g model group title:string description:text


1
2
3
4
5
6

invoke
create
create
invoke
create
create

active_record
db/migrate/20130529180541_create_groups.rb
app/models/group.rb
test_unit
test/models/group_test.rb
test/fixtures/groups.yml

Group model
rake db:migrate
1
2
3
4

== CreateGroups: migrating ===================================================


-- create_table(:groups)
-> 0.1951s
== CreateGroups: migrated (0.1952s) ==========================================

31

1 - Group - CRUD RESTful

groups controller

rails g controller groups


create
invoke
create
invoke
create
invoke
create
invoke
create
invoke
invoke
create
invoke
create

1
2
3
4
5
6
7
8
9
10
11
12
13
14

app/controllers/groups_controller.rb
erb
app/views/groups
test_unit
test/controllers/groups_controller_test.rb
helper
app/helpers/groups_helper.rb
test_unit
test/helpers/groups_helper_test.rb
assets
coffee
app/assets/javascripts/groups.js.coffee
scss
app/assets/stylesheets/groups.css.scss

groups routing

config/routes.rb
1

resources :groups

32

1 - Group - CRUD RESTful

33

Ch 1.1 () RESTful
Rails 1.2 RESTful 2.0

RESTful Rails

RESTful Rails

Rails RESTful

RESTful Rails
web
Rails
Rails Rails RESTful

RESTful Rails Developer

RESTful

1 - Group - CRUD RESTful

34

Rails RESTful

config/routes.rb resources

config/routes.rb resources :groups rails controller RESTful


controller resources)
1
2
3

Groupme::Application.routes.draw do
resources :groups
end

35

1 - Group - CRUD RESTful

RESTful

1 - Group - CRUD RESTful

Ch 1.1.1 Groups Controller index


app/controllers/groups_controller.rb
1
2
3

def index
@groups = Group.all
end

36

1 - Group - CRUD RESTful

view

touch app/views/groups/index.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13

<div class="span12">
<div class="group">
<%= link_to("New group", new_group_path , :class => "btn btn-mini btn-primary\
pull-right" ) %>
</div>
<table class="table">
<thead>
<tr>
<td> # </td>
<td> Title </td>
<td> Descroption </td>
</tr>
</thead>

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

<tbody>
<% @groups.each do |group| %>
<tr>
<td> # </td>
<td> <%= link_to(group.title, group_path(group)) %> </td>
<td> <%= group.description %> </td>
<td> <%= link_to("Edit", edit_group_path(group), :class => "btn btn-mini"\
) %>
<%= link_to("Delete", group_path(group), :class => "btn btn-mini", :meth\
od => :delete, :confirm => "Are you sure?" ) %>
</td>
</tr>
<% end %>
</tbody>
</table>

30
31

</div>

37

1 - Group - CRUD RESTful

Group.all group model


1
2
3

def index
@groups = Group.all
end

38

1 - Group - CRUD RESTful

Ch 1.1.2 Groups Controller show


app/controllers/groups_controller.rb show action
1
2
3

def show
@group = Group.find(params[:id])
end

view

touch app/views/groups/show.html.erb

<div class="span12">

2
3
4
5
6

<div class="group">
<%= link_to("Edit", edit_group_path(@group) , :class => "btn btn-mini btn-pri\
mary pull-right" ) %>
</div>

7
8

<h2> <%= @group.title %> </h2>

9
10

<p> <%= @group.description %> </p>

11
12

</div>

39

1 - Group - CRUD RESTful

40

Group.find(123) Post model id 123 http://groupme.dev/groups/123


groups controllershow action route 123
params[:id]
1
2
3

def show
@group = Group.find(params[:id])
end

1 - Group - CRUD RESTful

Ch 1.1.3 Groups Controller new


app/controllers/groups_controller.rb new action
1
2
3

def new
@group = Group.new
end

Group object

view

touch app/views/groups/new.html.erb

<div class="span12">

2
3
4
5

<%= simple_form_for @group do |f| %>


<%= f.input :title, :input_html => { :class => "input-xxlarge"} %>
<%= f.input :description, :input_html => { :class => "input-xxlarge"} %>

6
7
8
9
10
11

<div class="form-actions">
<%= f.submit "Submit", :disable_with => 'Submiting...', :class => "btn btn\
-primary" %>
</div>
<% end %>

12
13

</div>

41

1 - Group - CRUD RESTful

initial Post object


1
2
3

def new
@group = Group.new
end

42

1 - Group - CRUD RESTful

43

Ch 1.1.4 Groups Controller create


app/controllers/groups_controller.rb create action
1
2
3

def create
@group = Group.new(params[:group])
@group.save

4
5
6

redirect_to groups_path
end

Group title
group
title group new
app/models/group.rb group
1

class Group < ActiveRecord::Base

2
3

validates :title, :presence => true

4
5

end

app/controllers/groups_controller.rb create action

1
2

def create
@group = Group.new(group_params)

3
4
5
6
7
8
9

if @group.save
redirect_to groups_path
else
render :new
end
end

10
11
12

private

13
14
15
16
17

def group_params
params.require(:group).permit(:title, :description)
end

1 - Group - CRUD RESTful

44

app/views/groups/new.html.erb view
1

<div class="span12">

2
3
4
5

<%= simple_form_for @group do |f| %>


<%= f.input :title, :input_html => { :class => "input-xxlarge"} %>
<%= f.input :description, :input_html => { :class => "input-xxlarge"} %>

6
7
8
9
10
11

<div class="form-actions">
<%= f.submit "Submit", :disable_with => 'Submiting...', :class => "btn btn\
-primary" %>
</div>
<% end %>

12
13

</div>

RESTful Rails groups_path POST create Rails


form model form hash
params[:group]create action object params[:group] object
@group index action new action

1 - Group - CRUD RESTful

45

Ch 1.1.4 () Strong Parameters


2012 Github
Rails mass assignment
attr_accessible / attr_protected Developer
Rails 3.2.3 config.whitelist_attributes =
true model
model attr_accessible
model
mass-assign

Hack

mass-assignment etc.
:
attr_accessible
attr_accessible
attr_accessible
Admin mass-assignment
Rails 3.1 scoped mass assignment model
scoped mass assignment

http://blog.xdite.net/posts/2012/03/05/github-hacked-rails-security/

1 - Group - CRUD RESTful

46

controller model

controller controller
solution strong_parameters
Rails DHH
1
2
3
4

class PostsController < ActionController::Base


def create
Post.create(post_params)
end

5
6
7
8

def update
Post.find(params[:id]).update_attributes!(post_params)
end

9
10
11
12
13
14

private
def post_params
params[:post].slice(:title, :content)
end
end

slice hacker parameter


( exception)

https://github.com/rails/strong_parameters/

1 - Group - CRUD RESTful

47

Strong Parameters
strong_parameters permit
throw exception
1
2
3
4
5

class PeopleController < ActionController::Base


def update
person.update_attributes!(person_params)
redirect_to :back
end

6
7
8
9
10
11

private
def person_params
params.require(:person).permit(:name, :age)
end
end

Strong Parameters

controller Railscast

Nested Attributes
Orgngized to Class

http://railscasts.com/episodes/371-strong-parameters

1 - Group - CRUD RESTful

Ch 1.1.5 Groups Controller edit


app/controllers/groups_controller.rb edit action
1
2
3

def edit
@group = Group.find(params[:id])
end

query Group model object

view

touch app/views/groups/edit.html.erb

<div class="span12">

2
3
4
5

<%= simple_form_for @group do |f| %>


<%= f.input :title, :input_html => { :class => "input-xxlarge"} %>
<%= f.input :description, :input_html => { :class => "input-xxlarge"} %>

6
7
8
9
10
11

<div class="form-actions">
<%= f.submit "Submit", :disable_with => 'Submiting...', :class => "btn btn\
-primary" %>
</div>
<% end %>

12
13

</div>

48

1 - Group - CRUD RESTful

query Group model object


1
2
3

def edit
@group = Group.find(params[:id])
end

49

1 - Group - CRUD RESTful

50

Ch 1.1.6 Groups Controller update


app/controllers/groups_controller.rb update action
1
2

def update
@group = Group.find(params[:id])

3
4
5
6
7
8
9

if @group.update(group_params)
redirect_to group_path(@group)
else
render :edit
end
end

app/views/posts/edit.html.erb view
1

<div class="span12">

2
3
4
5

<%= simple_form_for @group do |f| %>


<%= f.input :title, :input_html => { :class => "input-xxlarge"} %>
<%= f.input :description, :input_html => { :class => "input-xxlarge"} %>

6
7
8
9
10
11

<div class="form-actions">
<%= f.submit "Submit", :disable_with => 'Submiting...', :class => "btn btn\
-primary" %>
</div>
<% end %>

12
13

</div>

RESTful Rails group_path PUT update form


hash @group params[:group]
show action edit action

1 - Group - CRUD RESTful

Ch 1.1.7 Groups Controller destroy


app/controllers/groups_controller.rb destroy action
1
2

def destroy
@group = Group.find(params[:id])

3
4

@group.destroy

5
6
7

redirect_to groups_path
end

Group CRUD

Group Index

config/routes.rb root group index


root :to => "groups#index"

51

1 - Group - CRUD RESTful

52

app/views/posts/index.html.erb view
<tr>
<td>
<td>
<td>
<td>

1
2
3
4
5
6
7
8
9
10

# </td>
<%= link_to(group.title, group_path(group)) %> </td>
<%= group.description %> </td>
<%= link_to("Edit", edit_group_path(group), :class => "btn btn-mini"\

) %>
<%= link_to("Delete", group_path(group), :class => "btn btn-mini", :meth\
od => :delete, :confirm => "Are you sure?" ) %>
</td>
</tr>

RESTful Rails group_path DELETE


destroy link_to "Destroy"

1 - Group - CRUD RESTful

RESTful

53

on Rails

Representational State Transfer REST Roy Fielding 2000


Web Services REST
Web Services SOAP XML-RPC
API

What is REST?
REST
1.
2.
3.
4.
5.

(resource)
resource identifier
(generic connector interface)
resource idetifier
(stateless)

web services
resource indentifier URI
generic connector interface HTTP

54

1 - Group - CRUD RESTful

RESTful Web Services


RESTful Web Services HTTP REST Web Services

URIhttp://example.com/resources/
Web Services accept return media typeJSONXML YAML
Web Services resources request methid : POSTGETPUT
DELETE
RESTful Web HTTP
GET

GET

URI
http://example.com/resources/
URI
http://example.com/resources/142

media type
XMLJSON

PUT

URI
http://example.com/resources/
URI
http://example.com/resources/142

URI

POST

URI
http://example.com/resources/
URI
http://example.com/resources/142

/
URL

PUT

POST

55

1 - Group - CRUD RESTful

DELETE

DELETE

URI http://example.com/resources/
URI http://example.com/resources/142

1 - Group - CRUD RESTful

56

DHH Discovering a world of Resources on Rails Constraints are liberating

Rails REST REST


DHH REST Rais

1
2

def add_friend
end

3
4
5

def remove_friend
end

6
7
8

def create_post
end

9
10
11

def delete_post
end

REST resource
1
2
3

class FriendShipController
def create
end

4
5
6
7

def destroy
end
end

8
9
10
11

class PostController
def create
end

12
13
14
15

def destroy
end
end
http://www.slideshare.net/vishnu/discovering-a-world-of-resources-on-rails

1 - Group - CRUD RESTful

Action

57

as Resource

REST resource data resource


data + representation ( html, xml, json )
HTML json
xmlRails responder
Resource Oriented Architecture (ROA REST guideline) A resource
can use file extension in the URI, instead of Content-Type negotiation
Rails responder action file extension ( .json, .xml, .csv etc.)
representation
1
2
3
4
5

class PostsController < ApplicationController


# GET /posts
# GET /posts.xml
def index
@posts = Post.all

6
7
8
9
10
11

respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @posts }
end
end

CRUD action + HTTP verb + Helper URL

Helper
REST Rails REST implementation
controller method

index
show
new
edit
create
update
destroy

CRUDweb services
Rails HTTP generic connector interface HTTP verb GETPOST
PUTDELETE

1 - Group - CRUD RESTful

GET
1
2
3
4

<%=
<%=
<%=
<%=

link_to("List", posts_path) %>


link_to("Show", post_path(post)) %>
link_to("New", new_post_path) %>
link_to("Edit", edit_post_path(post)) %>

POST
1

<%= form_for @post , :url => posts_path , :html => {:method => :post} do |f| %>

PUT
1
2

<%= form_for @post , :url => post_path(@post) , :html => {:method => :put} do |\
f| %>

Destroy
1

<%= link_to("Destroy", post_path(@post), :method => :delete )

58

1 - Group - CRUD RESTful

59

Form Model Attribute


Rails Model Attribute
1

<h1>New post</h1>

2
3
4
5
6
7
8

<%= form_for @post , :url => posts_path do |f| %>


<%= f.error_messages %>
<div><label>subject</label><%= f.text_field :subject %></div>
<div><label>content</label><%= f.text_area :content %> </div>
<%= f.submit "Submit", :disable_with => 'Submiting...' %>
<% end -%>

9
10
11

<%= link_to 'Back', posts_path %>

form_for parameter: params[:post]


1
2
3
4
5
6
7
8
9

def create
@post = Post.new(params[:post])
if @post.save
flash[:notice] = 'Post was successfully created.'
redirect_to post_path(@post)
else
render :action => "new"
end
end

REST
Wikipedia: REST
Wikipedia: ROA
DHH: Discovering a world of Resource on Rails
ihower: Rails RESTful
ihower: REST RESTful

http://baike.baidu.com/view/1077487.htm
http://zh.wikipedia.org/wiki/REST
http://en.wikipedia.org/wiki/Resource-oriented_architecture
http://en.wikipedia.org/wiki/Resource-oriented_architecture
http://ihower.tw/blog/archives/1566
http://ihower.tw/blog/archives/1542

1 - Group - CRUD RESTful

More

60

about RESTful on Rails

action RESTful route

Collection & Member


Member
URI /photos/1/preview GET preview_photo_path(photo) preview_photo_url(photo) Url Helper
1
2
3
4
5

resources :photos do
member do
get 'preview'
end
end

1
2
3

resources :photos do
get 'preview', :on => :member
end

Collection
URI /photos/search GET search_photos_path search_photos_url Url Helper
1
2
3
4
5

resources :photos do
collection do
get 'search'
end
end

1 - Group - CRUD RESTful

61

1
2
3

resources :photos do
get 'search', :on => :collection
end

Nested Resources
resources Post Comment
1
2
3

resources :posts do
resources :comments
end

/posts/123/comments GET post_comments_path(post) post_comments_url(post) Url Helper


URL 123 post
comments
Post id
1

class CommentsController < ApplicationController

2
3

before_filter :find_post

4
5
6
7

def index
@comments = @post.comments
end

8
9

protected

10
11
12
13
14

def find_post
@post = Post.find(params[:post_id])
end
end

1 - Group - CRUD RESTful

62

Namespace Resources
/admin/posts/1/edit URL Nested Resources
URL Namespace
1
2
3
4
5

namespace :admin do
# Directs /admin/products/* to Admin::PostsController
# (app/controllers/admin/posts_controller.rb)
resources :posts
end

/admin/posts/123/edit GET edit_admin_post_path(post) edit_admin_post_url(post) Url Helper

Rails Guide: Rails Routing from the Outside In


http://guides.rubyonrails.org/routing.html

2 - Group -
RESTFul

Group CRUD Group


http://groupme.dev/group/1/post/2

has_many, belongs_to
resources resources )
before_action

2 - Group - RESTFul

Ch 2.1 Post

Post model

rails g model post content:text group_id:integer


1
2
3
4
5
6

invoke
create
create
invoke
create
create

active_record
db/migrate/20130529200707_create_posts.rb
app/models/post.rb
test_unit
test/models/post_test.rb
test/fixtures/posts.yml

Post model
rake db:migrate
1
2
3
4

== CreatePosts: migrating ====================================================


-- create_table(:posts)
-> 0.1297s
== CreatePosts: migrated (0.1298s) ===========================================

64

2 - Group - RESTFul

posts controller

rails g controller posts


create
invoke
create
invoke
create
invoke
create
invoke
create
invoke
invoke
create
invoke
create

1
2
3
4
5
6
7
8
9
10
11
12
13
14

app/controllers/posts_controller.rb
erb
app/views/posts
test_unit
test/controllers/posts_controller_test.rb
helper
app/helpers/posts_helper.rb
test_unit
test/helpers/posts_helper_test.rb
assets
coffee
app/assets/javascripts/posts.js.coffee
scss
app/assets/stylesheets/posts.css.scss

posts routing

config/routes.rb
1

resources :groups

1
2
3

resources :groups do
resources :posts
end

65

2 - Group - RESTFul

Group Post

app/models/group.rb has_many :posts


1

class Group < ActiveRecord::Base

2
3
4

has_many :posts
validates :title, :presence => true

5
6

end

app/models/post.rb belongs_to :group


1
2
3

class Post < ActiveRecord::Base


belongs_to :group
end

66

2 - Group - RESTFul

Ch 2.1.1 Groups controller show Post


app/controllers/groups_controller.rb show
1
2
3
4

def show
@group = Group.params([:id])
@posts = @group.posts
end

67

2 - Group - RESTFul

view

app/views/groups/show.html.erb
1
2
3

<div class="span12">
<div class="group pull-right">
<%= link_to("Edit", edit_group_path(@group) , :class => "btn btn-mini ")%>

4
5
6
7
8
9
10

<%= link_to("New Post", new_group_post_path(@group) , :class => "btn btn-mini\


btn-primary")%>
&nbsp;
</div>
<h2> <%= @group.title %> </h2>
<p> <%= @group.description %> </p>

11
12
13

<table class="table">

14
15
16
17
18
19
20
21
22
23
24
25
26
27

<tbody>
<% @posts.each do |post| %>
<tr>
<td> <%= post.content %> </td>
<td> <%= link_to("Edit", edit_group_post_path(post.group, post), :class => \
"btn btn-mini")%>
<%= link_to("Delete", group_post_path(post.group, post), :class => "btn b\
tn-mini", :method => :delete, :confirm => "Are you sure?" ) %> </td>
</tr>
<% end %>
</tbody>
</table>
</div>

68

2 - Group - RESTFul

Ch 2.1.2 Posts Controller new


app/controllers/posts_controller.rb new action
1
2
3
4

def new
@group = Group.find(params[:group_id])
@post = @group.posts.build
end

view

touch app/views/posts/new.html.erb

<div class="span12">

2
3
4

<%= simple_form_for [@group,@post] do |f| %>


<%= f.input :content, :input_html => { :class => "input-xxlarge"} %>

5
6
7
8
9
10

<div class="form-actions">
<%= f.submit "Submit", :disable_with => 'Submiting...', :class => "btn btn\
-primary" %>
</div>
<% end %>

11
12

</div>

69

2 - Group - RESTFul

70

Ch 2.1.3 Posts Controller create


app/controllers/posts_controller.rb create action
1
2
3

def create
@group = Group.find(params[:group_id])
@post = @group.posts.new(post_params)

4
5
6
7
8
9
10

if @post.save
redirect_to group_path(@group)
else
render :new
end
end

11
12

private

13
14
15
16
17

def post_params
params.require(:post).permit(:content)
end

Post Post content


post
content post new
app/models/post.rb post
1

class Post < ActiveRecord::Base

2
3
4

belongs_to :group
validates :content, :presence => true

5
6

end

2 - Group - RESTFul

71

Rails ORM Validation API Developer

http://edgeguides.rubyonrails.org/active_record_validations.html

2 - Group - RESTFul

Ch 2.1.4 Posts Controller edit


app/controllers/groups_controller.rb edit action
1
2
3
4

def edit
@group = Group.find(params[:group_id])
@post = @group.posts.find(params[:id])
end

view

touch app/views/posts/edit.html.erb
1

<div class="span12">

2
3
4

<%= simple_form_for [@group,@post] do |f| %>


<%= f.input :content, :input_html => { :class => "input-xxlarge"} %>

5
6
7
8
9
10

<div class="form-actions">
<%= f.submit "Submit", :disable_with => 'Submiting...', :class => "btn btn\
-primary" %>
</div>
<% end %>

11
12

</div>

72

2 - Group - RESTFul

Ch 2.1.5 Posts Controller update


app/controllers/posts_controller.rb update action
1
2
3

def update
@group = Group.find(params[:group_id])
@post = @group.posts.find(params[:id])

4
5
6
7
8
9
10

if @post.update(post_params)
redirect_to group_path(@group)
else
render :edit
end
end

73

2 - Group - RESTFul

Ch 2.1.6 Posts Controller destroy


app/controllers/groups_controller.rb destroy action
1
2
3

def destroy
@group = Group.find(params[:group_id])
@post = @group.posts.find(params[:id])

4
5

@post.destroy

6
7
8

redirect_to group_path(@group)
end

Post CRUD

74

2 - Group - RESTFul

Ch 2.1.7 before_action
Posts action
1

@group = Group.find(params[:group_id])

before_action
private find_group method
1
2
3

def find_group
@group = Group.find(params[:group_id])
end

class PostsController < ApplicationController


1

before_action :find_group

action
1

@group = Group.find(params[:group_id])

class PostsController < ApplicationController

2
3

before_action :find_group

4
5
6
7

def new
@post = @group.posts.build
end

8
9

def create

10
11

@post = @group.posts.new(post_params)

12
13
14
15

if @post.save
redirect_to group_path(@group)
else

75

2 - Group - RESTFul
16
17
18

render :new
end
end

19
20
21
22

def edit
@post = @group.posts.find(params[:id])
end

23
24

def update

25
26

@post = @group.posts.find(params[:id])

27
28
29
30
31
32
33

if @post.update(post_params)
redirect_to group_path(@group)
else
render :edit
end
end

34
35
36

def destroy

37
38

@post = @group.posts.find(params[:id])

39
40

@post.destroy

41
42
43

redirect_to group_path(@group)
end

44
45
46

private

47
48
49
50
51

def find_group
@group = Group.find(params[:group_id])
end

52
53
54
55
56

def post_params
params.require(:post).permit(:content)
end
end

76

2 - Group - RESTFul

before_action controller
before_action only action
1

before_action :find_group, :only => [:edit, :update]

except action
1

before_action :find_group, :except => [:show, :index]

77

3 - Group Post

Group

/ Group Post
GroupPost

gem
devise
method login_required
before_action login_required
session current_user

devise

https://github.com/plataformatec/devise/wiki

3 - Group Post

79

Ch 3.0 devise Rails 4

gem

Rails gem Bundler Gemfile


gem bundle install

devise

devise Rails
LockableConfirmableRecoverable
Facebook OmniAuthable
Bootstrappers devise gem

Gemfile
1

gem 'devise'

bundle
bundle install

rails g devise:install

user model
rails generate devise user

devise
<%= link_to( "Sign Up" ,new_user_registration_path) %>
<%= link_to( "Login", new_user_session_path ) %>
<%= link_to("Logout",destroy_user_session_path, :method => :delete ) %>

devise method
1. current_user.blank?
2. current_user
https://github.com/plataformatec/devise

3 - Group Post

80

login_required
Bootstrappers app/controller/application_controller.rb
methodlogin_required
1
2
3
4
5
6
7
8
9
10
11
12
13
14

def login_required
if current_user.blank?
respond_to do |format|
format.html {
authenticate_user!
}
format.js{
render :partial => "common/not_logined"
}
format.all {
head(:unauthorized)
}
end
end

15
16

end

action before_action

3 - Group Post

81

devise

devise email password name Bootstrappers


name devise name
user name
devise view gem view generator

rails g devise:views

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

invoke
create
create
invoke
create
create
create
create
create
create
create
create
create
create
create
create
invoke
create
create
create
create

Devise::Generators::SharedViewsGenerator
app/views/devise/shared
app/views/devise/shared/_links.erb
simple_form_for
app/views/devise/confirmations
app/views/devise/confirmations/new.html.erb
app/views/devise/passwords
app/views/devise/passwords/edit.html.erb
app/views/devise/passwords/new.html.erb
app/views/devise/registrations
app/views/devise/registrations/edit.html.erb
app/views/devise/registrations/new.html.erb
app/views/devise/sessions
app/views/devise/sessions/new.html.erb
app/views/devise/unlocks
app/views/devise/unlocks/new.html.erb
erb
app/views/devise/mailer
app/views/devise/mailer/confirmation_instructions.html.erb
app/views/devise/mailer/reset_password_instructions.html.erb
app/views/devise/mailer/unlock_instructions.html.erb

3 - Group Post

82

app/views/devise/registrations/new.html.erb name

<h2>Sign up</h2>

2
3
4
5

<%= simple_form_for(resource, :as => resource_name, :url => registration_path(res\


ource_name)) do |f| %>
<%= f.error_notification %>

6
7
8
9
10
11
12

<div class="form-inputs">
<%= f.input :email, :required => true, :autofocus => true %>
<%= f.input :name, :required => true %>
<%= f.input :password, :required => true %>
<%= f.input :password_confirmation, :required => true %>
</div>

13
14
15
16
17

<div class="form-actions">
<%= f.button :submit, "Sign up" %>
</div>
<% end %>

18
19

<%= render "users/shared/links" %>

strong_parameters devise hack

app/controller/application_controller
1

before_filter :configure_permitted_parameters, if: :devise_controller?

2
3
4

protected

5
6
7
8
9

def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :passw\
ord, :password_confirmation) }
end

https://github.com/plataformatec/devise/tree/rails4#strong-parameters

3 - Group Post

Ch 3.1 Action
/ Group Post
Groups controller
1

class GroupsController < ApplicationController

2
3

before_action :login_required, :only => [:new, :create, :edit,:update,:destroy]

Posts controller
1

class PostsController < ApplicationController

2
3

before_action :login_required, :only => [:new, :create, :edit,:update,:destroy]

83

3 - Group Post

Ch 3.2 Group User


migrationrails g migration add_user_id_to_group
invoke
create

1
2

active_record
db/migrate/20130531141923_add_user_id_to_group.rb

1
2
3
4
5

class AddUserIdToGroup < ActiveRecord::Migration


def change
add_column :groups, :user_id, :integer
end
end

rake db:migrate
1
2
3
4

== AddUserIdToGroup: migrating ===============================================


-- add_column(:groups, :user_id, :integer)
-> 0.0213s
== AddUserIdToGroup: migrated (0.0214s) ======================================

app/models/user.rb has_many :groups

1
2
3
4

class User < ActiveRecord::Base


# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable

5
6

has_many :groups

7
8

extend OmniauthCallbacks

9
10
11

devise :database_authenticatable, :registerable,


:recoverable, :rememberable, :trackable, :validatable, :omniauthable

12
13
14

end

84

3 - Group Post

app/models/group.rb
1

belongs_to :owner, :class_name => "User", :foreign_key => :user_id

2
3
4
5

def editable_by?(user)
user && user == owner
end

class Group < ActiveRecord::Base

2
3
4

belongs_to :owner, :class_name => "User", :foreign_key => :user_id


has_many :posts

5
6

validates :title, :presence => true

7
8
9
10
11
12

def editable_by?(user)
user && user == owner
end
end

85

3 - Group Post

Groups action

create
1
2
3
4
5
6
7
8

def create
@group = current_user.groups.build(group_params)
if @group.save
redirect_to groups_path
else
render :new
end
end

edit
1
2
3

def edit
@group = current_user.groups.find(params[:id])
end

update
1
2

def update
@group = current_user.groups.find(params[:id])

3
4
5
6
7
8
9

if @group.update(group_params)
redirect_to group_path(@group)
else
render :edit
end
end

86

3 - Group Post

destroy
1
2

def destroy
@group = current_user.groups.find(params[:id])

3
4

@group.destroy

5
6
7

redirect_to groups_path
end

87

3 - Group Post

app/views/groups/index.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

<div class="span12">
<div class="group">
<%= link_to("New group", new_group_path , :class => "btn btn-mini btn-primary\
pull-right")%>
</div>
<table class="table">
<thead> <tr>
<td> # </td>
<td> Title </td>
<td> Descroption </td>
<td> Owner </td>
</tr>
</thead>
<tbody>
<% @groups.each do |group| %>
<tr>
<td> # </td>
<td> <%= link_to(group.title, group_path(group)) %> </td>
<td> <%= group.description %> </td>
<td> <%= group.owner.name %> </td>
<td>
<% if current_user && group.editable_by?(current_user) %>
<%= link_to("Edit", edit_group_path(group), :class => "btn btn-mini")%>
<%= link_to("Delete", group_path(group), :class => "btn btn-mini", :metho\
d => :delete, :confirm => "Are you sure?" ) %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>

88

3 - Group Post

89

@group = Group.find(params[:id])
action action
code
1
2

def edit
@group = Group.find(params[:id])

3
4
5
6
7

if @group.user != current_user
flash[:warning] = "No Permission"
redirec_to root_path
end

8
9

end

current_user.groups

1
2
3

def edit
@group = current_user.groups.find(params[:id])
end

Rails Production 404

create
1
2
3
4
5
6
7
8
9

def create
@group = Group.new(group_params)
@group.user = current_user
if @group.save
redirect_to groups_path
else
render :new
end
end

3 - Group Post

association
1
2
3
4
5
6
7
8

def create
@group = current_user.groups.build(group_params)
if @group.save
redirect_to groups_path
else
render :new
end
end

90

3 - Group Post

Ch 3.3 Post User


migrationrails g migration add_user_id_to_post
invoke
create

1
2

active_record
db/migrate/20130531153435_add_user_id_to_post.rb

1
2
3
4
5

class AddUserIdToPost < ActiveRecord::Migration


def change
add_column :posts, :user_id, :integer
end
end

rake db:migrate
1
2
3
4

== AddUserIdToPost: migrating ================================================


-- add_column(:posts, :user_id, :integer)
-> 0.0176s
== AddUserIdToPost: migrated (0.0177s) =======================================

app/models/user.rb has_many :posts

1
2
3
4

class User < ActiveRecord::Base


# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable

5
6
7

has_many :groups
has_many :posts

8
9

extend OmniauthCallbacks

10
11
12

devise :database_authenticatable, :registerable,


:recoverable, :rememberable, :trackable, :validatable, :omniauthable

13
14
15

end

app/models/post.rb

91

3 - Group Post
1

belongs_to :author, :class_name => "User", :foreign_key => :user_id

2
3
4
5

def editable_by?(user)
user && user == author
end

class Post < ActiveRecord::Base

2
3
4

belongs_to :group
validates :content, :presence => true

5
6

belongs_to :author, :class_name => "User", :foreign_key => :user_id

7
8
9
10

def editable_by?(user)
user && user == author
end

11
12

end

92

3 - Group Post

Posts action

create
1

def create

2
3
4

@post = @group.posts.new(post_params)
@post.author = current_user

5
6
7
8
9
10
11

if @post.save
redirect_to group_path(@group)
else
render :new
end
end

edit
1
2
3

def edit
@post = current_user.posts.find(params[:id])
end

update
1

def update

2
3

@post = current_user.posts.find(params[:id])

4
5
6
7
8
9
10

if @post.update(post_params)
redirect_to group_path(@group)
else
render :edit
end
end

93

3 - Group Post

destroy
1

def destroy

2
3

@post = current_user.posts.find(params[:id])

4
5

@post.destroy

6
7
8

redirect_to group_path(@group)
end

app/views/groups/show.html.erb
1

<div class="span12">

2
3
4
5
6
7

<div class="group pull-right">


<% if current_user && @group.editable_by?(current_user) %>
<%= link_to("Edit", edit_group_path(@group) , :class => "btn btn-mini ")%>
<% end %>

8
9
10
11
12

<%= link_to("New Post", new_group_post_path(@group) , :class => "btn btn-mini b\


tn-primary" if current_user )%>
&nbsp;
</div>

13
14

<h2> <%= @group.title %> </h2>

15
16

<p> <%= @group.description %> </p>

17
18
19

<table class="table">

20
21
22
23
24

<tbody>
<% @posts.each do |post| %>
<tr>
<td>

25

<span clas="author"> <strong> Author : <%= post.author.name %> </strong> \

26
27
28
29

</span>
<p>
<%= post.content %>

94

3 - Group Post
30
31

</p>
</td>

32
33
34
35
36
37
38
39
40
41
42
43

<% if current_user && post.editable_by?(current_user) %>


<td> <%= link_to("Edit", edit_group_post_path(post.group, post), :class => \
"btn btn-mini")%>
<%= link_to("Delete", group_post_path(post.group, post), :class => "btn b\
tn-mini", :method => :delete, :confirm => "Are you sure?" ) %> </td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
</div>

95

4 - User

user group /
user

has_many_belongs_to
custom routes

class PostsController < ApplicationController

2
3
4
5

before_action :find_group
before_action :login_required, :only => [:new, :create, :edit,:update,:destroy]
before_action :member_required, :only => [:new, :create ]

6
7
8
9
10

def new
@post = @group.posts.build
end

11
12
13
14

def create
@post = @group.posts.new(post_params)
@post.author = current_user

15
16
17
18
19
20
21

if @post.save
redirect_to group_path(@group)
else
render :new
end
end

22
23
24
25

def edit
@post = current_user.posts.find(params[:id])
end

4 - User
26
27
28

def update
@post = current_user.posts.find(params[:id])

29
30
31
32
33
34
35

if @post.update(post_params)
redirect_to group_path(@group)
else
render :edit
end
end

36
37
38

def destroy
@post = current_user.posts.find(params[:id])

39
40

@post.destroy

41
42
43

redirect_to group_path(@group)
end

44
45

private

46
47
48
49

def post_params
params.require(:post).permit(:content)
end

50
51
52
53

def find_group
@group = Group.find(params[:group_id])
end

54
55
56
57
58
59
60
61

def member_required
if !current_user.is_member_of?(@group)
flash[:warning] = " You are not member of this group!"
redirect_to group_path(@group)
end
end
end

97

4 - User

98

Ch 4.1
2 3 Group Post

4.1.1 has_many :through


group_user modelrails g model group_user group_id:integer user_id:integer
migrationrake db:migrate
1
2
3
4

== CreateGroupUsers: migrating ===============================================


-- create_table(:group_users)
-> 0.0238s
== CreateGroupUsers: migrated (0.0239s) ======================================

User model
1
2

has_many :group_users
has_many :participated_groups, :through => :group_users, :source => :group

class User < ActiveRecord::Base

2
3
4

has_many :groups
has_many :posts

5
6
7

has_many :group_users
has_many :participated_groups, :through => :group_users, :source => :group

8
9

extend OmniauthCallbacks

10
11
12

devise :database_authenticatable, :registerable,


:recoverable, :rememberable, :trackable, :validatable, :omniauthable

13
14
15
16
17
18

def join!(group)
participated_groups << group
end

4 - User
19
20
21

def quit!(group)
participated_groups.delete(group)
end

22
23
24
25

def is_member_of?(group)
participated_groups.include?(group)
end

26
27

end

99

4 - User

app/models/group.rb
1
2

has_many :group_users
has_many :members, :through => :group_users, :source => :group

class Group < ActiveRecord::Base

2
3
4
5
6

belongs_to :owner, :class_name => "User", :foreign_key => :user_id


has_many :posts
has_many :group_users
has_many :members, :through => :group_users, :source => :group

7
8

validates :title, :presence => true

9
10
11
12
13
14

def editable_by?(user)
user && user == owner
end
end

app/models/group_user.rb
1
2
3
4

class GroupUser < ActiveRecord::Base


belongs_to :group
belongs_to :user
end

Many to Many
1
2

has_many :group_users
has_many :members, :through => :group_users, :source => :user

Rails has_one,belongs_to has_manyhas_many :through

100

4 - User

101

Ch 4.1.2 model method after create


method User model
1
2
3

def join!(group)
participated_groups << group
end

4
5
6
7

def quit!(group)
participated_groups.delete(group)
end

8
9
10
11

def is_member_of?(group)
participated_groups.include?(group)
end

controller current_user.join!(group) group

Group group group

Group group
app/controller/groups_controller.rb create action current_user.join!(@group)
1
2
3
4
5
6
7
8
9

def create
@group = current_user.groups.build(group_params)
if @group.save
current_user.join!(@group)
redirect_to groups_path
else
render :new
end
end

ActiveRecord after_create

4 - User
1

102

class Group < ActiveRecord::Base

2
3
4

belongs_to :owner, :class_name => "User", :foreign_key => :user_id


has_many :posts

5
6
7

has_many :group_users
has_many :members, :through => :group_users, :source => :user

8
9
10

validates :title, :presence => true

11
12

after_create :join_owner_to_group

13
14
15
16

def editable_by?(user)
user && user == owner
end

17
18
19
20

def join_owner_to_group
members << owner
end

21
22

end

after_create
after_create ActiveRecord callbacks callbacks

before_create
before_update
after_create
after_update

4 - User

Ch 4.1.3 join quit action


Groups Controller action
1
2

def join
@group = Group.find(params[:id])

3
4
5
6
7
8
9
10

if !current_user.is_member_of?(@group)
current_user.join!(@group)
else
flash[:warning] = "You already joined this group."
end
redirect_to group_path(@group)
end

11
12
13

def quit
@group = Group.find(params[:id])

14
15
16
17
18
19

if current_user.is_member_of?(@group)
current_user.quit!(@group)
else
flash[:warning] = "You are not member of this group."
end

20
21

redirect_to group_path(@group)

22
23

end

config/routes.rb resources :groups


1
2
3
4
5
6
7

resources :groups do
member do
post :join
post :quit
end
resources :posts
end

app/views/groups/show.html.erb

103

4 - User
1
2
3
4
5
6
7
8
9
10
11

104

<% if current_user %>


<div class="group pull-right">
<% if current_user.is_member_of?(@group) %>
<%= link_to("Quit Group", quit_group_path(@group), :method => :post, :class\
=> "btn btn-mini") %>
<% else %>
<%= link_to("Join Group", join_group_path(@group), :method => :post, :class\
=> "btn btn-mini") %>
<% end %>
</div>
<% end %>

app/views/groups/show.html.erb
1
2

<%= link_to("New Post", new_group_post_path(@group) , :class => "btn btn-mini b\


tn-primary") if current_user %>

1
2

<%= link_to("New Post", new_group_post_path(@group) , :class => "btn btn-mini b\


tn-primary") if current_user.is_member_of?(@group) %>

controller member_required

Post controller before_action


1

before_action :member_required, :only => [:new, :create ]

private member_required method


1
2
3
4
5
6

def member_required
if !current_user.is_member_of?(@group)
flash[:warning] = " You are not member of this group!"
redirect_to group_path(@group)
end
end

5 - Account

user account
user account
Group post
DESC

counter_cache count
ORM

http://railscasts.com/episodes/23-counter-cache-column

5 - Account

Ch 5.1 User Group


account/groups controllerrails g controller account/groups
1
2
3
4
5
6
7
8
9
10
11
12
13
14

identical
invoke
exist
invoke
identical
invoke
identical
invoke
identical
invoke
invoke
identical
invoke
identical

app/controllers/account/groups_controller.rb
erb
app/views/account/groups
test_unit
test/controllers/account/groups_controller_test.rb
helper
app/helpers/account/groups_helper.rb
test_unit
test/helpers/account/groups_helper_test.rb
assets
coffee
app/assets/javascripts/account/groups.js.coffee
scss
app/assets/stylesheets/account/groups.css.scss

app/controllers/account/groups_controller.rb
1
2

class Account::GroupsController < ApplicationController


before_action :login_required

3
4
5
6
7

def index
@groups = current_user.participated_groups
end
end
touch app/views/account/groups/index.html.erb

<div class="span12">

2
3

<h2> My Groups </h2>

4
5
6
7
8
9
10

<table class="table">
<thead> <tr>
<td> # </td>
<td> Title </td>
<td> Descroption </td>
<td> Post Count </td>

106

5 - Account
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

<td> Last Update </td>


</tr>
</thead>
<tbody>
<% @groups.each do |group| %>
<tr>
<td> # </td>
<td> <%= link_to(group.title, group_path(group)) %> </td>
<td> <%= group.description %> </td>
<td> <%= group.posts.count %> </td>
<td> <%= group.updated_at %> </td>
</tr>
<% end %>
</tbody>
</table>
</div>

config/routes.rb
1
2
3

namespace :account do
resources :groups
end

app/views/common/_user_nav.html.erb
<%= render_list :class => "dropdown-menu" do |li|
li << link_to("Logout",destroy_user_session_path, :method => :delete )
end %>

1
2
3

1
2
3
4

<%= render_list :class => "dropdown-menu" do |li|


li << link_to("My Group", account_groups_path)
li << link_to("Logout",destroy_user_session_path, :method => :delete )
end %>

107

5 - Account

Ch 5.2 User
account/posts controllerrails g controller account/posts
create
invoke
create
invoke
create
invoke
create
invoke
create
invoke
invoke
create
invoke
create

1
2
3
4
5
6
7
8
9
10
11
12
13
14

app/controllers/account/posts_controller.rb
erb
app/views/account/posts
test_unit
test/controllers/account/posts_controller_test.rb
helper
app/helpers/account/posts_helper.rb
test_unit
test/helpers/account/posts_helper_test.rb
assets
coffee
app/assets/javascripts/account/posts.js.coffee
scss
app/assets/stylesheets/account/posts.css.scss

app/controllers/account/groups_controller.rb
1

class Account::PostsController < ApplicationController

2
3

before_action :login_required

4
5
6
7

def index
@posts = current_user.posts
end

8
9

end

108

109

5 - Account

touch app/views/account/posts/index.html.erb
1

<div class="span12">

2
3

<table class="table">

4
5
6

<thead>
<tr>

7
8
9
10
11
12

<td> Content </td>


<td> Group name </td>
<td> Last Update </td>
</tr>
</thead>

13
14
15
16
17

<tbody>
<% @posts.each do |post| %>
<tr>
<td>
<p>
<%= post.content %>

</p>

</td>

18
19

<td>

<%= post.group.title %>

</td>

20
21

<td> <%= post.updated_at %> </td>

22
23
24
25
26
27
28
29
30
31
32

<td> <%= link_to("Edit", edit_group_post_path(post.group, post), :class => \


"btn btn-mini")%>
<%= link_to("Delete", group_post_path(post.group, post), :class => "btn b\
tn-mini", :method => :delete, :confirm => "Are you sure?" ) %> </td>
</tr>
<% end %>
</tbody>
</table>
</div>

config/routes.rb resources :posts


1
2
3
4

namespace :account do
resources :groups
resources :posts
end

5 - Account

app/common/_user_nav.html.erb
<%= render_list :class => "dropdown-menu" do |li|
li << link_to("My Group", account_groups_path)
li << link_to("Logout",destroy_user_session_path, :method => :delete )
end %>

1
2
3
4

1
2
3
4
5

<%= render_list :class => "dropdown-menu" do |li|


li << link_to("My Group", account_groups_path)
li << link_to("My Post", account_posts_path)
li << link_to("Logout",destroy_user_session_path, :method => :delete )
end %>

110

5 - Account

111

Ch 5.3 DESC
My Post

app/controllers/account/posts_controller.rb
1

class Account::PostsController < ApplicationController

2
3

before_action :login_required

4
5
6
7

def index
@posts = current_user.posts.order("updated_at DESC")
end

8
9

end

5 - Account

112

Ch 5.4 Group DESC


app/views/account/groups/index.html.erb <%= group.posts.count
%> code
1

<div class="span12">

2
3
4
5
6
7
8
9
10
11
12
13
14
15

<tbody>
<% @groups.each do |group| %>
<tr>
<td> # </td>
<td> <%= link_to(group.title, group_path(group)) %> </td>
<td> <%= group.description %> </td>
<td> <%= group.posts.count %> </td>
<td> <%= group.updated_at %> </td>
</tr>
<% end %>
</tbody>
</table>
</div>
group.posts.count code SQL query

(0.2ms)

SELECT COUNT(*) FROM `posts` WHERE `posts`.`group_id` = 1

application count

groups table
posts_count create action posts_count +1
1

def create

2
3
4

@post = @group.posts.new(post_params)
@post.author = current_user

5
6
7
8
9
10
11
12

if @post.save
Group.increment_counter(:posts_count, @group.id)
redirect_to group_path(@group)
else
render :new
end
end

5 - Account

113

destroy action posts_count - 1


1

def destroy

2
3
4

@post = current_user.posts.find(params[:id])
@post.destroy

5
6
7
8

Group.decrement_counter(:posts_count, @group.id)
redirect_to group_path(@group)
end

Rails counter_cache
count posts_count
rails g migration add_posts_count_to_group
invoke
create

1
2

active_record
db/migrate/20130531183331_add_posts_count_to_group.rb

1
2
3
4
5

class AddPostsCountToGroup < ActiveRecord::Migration


def change
add_column :groups, :posts_count, :integer, :default => 0
end
end

rake db:migrate
1
2
3
4

== AddPostsCountToGroup: migrating ===========================================


-- add_column(:groups, :posts_count, :integer, {:default=>0})
-> 0.0232s
== AddPostsCountToGroup: migrated (0.0232s) ==================================

app/models/post.rb
1

belongs_to :group, :counter_cache => true

post create destroy +1 / -1 controller

5 - Account

114

counter_cache <%= group.posts.size %> posts_count


MySQL COUNT
posts_count index
DESC
1
2
3

def index
@groups = current_user.participated_groups.order("posts_count DESC")
end

6 - Refactor code

helper / helper html


partial html
scope query

helper
partial
scope condition SQL query

6 - Refactor code

116

Ch 6.1 helper code

post.updated_at group.updated_at

2013-05-31 14:47:31 UTC

date.to_s

post.updated_at.to_s(:long) May 31, 2013 18:04


group.updated_at.to_s(:short) 31 May 18:04

simple_format
post.content
1
2
3

a
b
c

HTML
1

a b c

Enter \r \n HTML HTML


<br>
nl2br helper Rails
simple_format helper
<%= simple_format(post.content) %>

truncate
group.title

Rails truncate helper
<%= truncate(@group.title, :length => 17 ) %>

6 - Refactor code

117

Ch 6.2 helper html


Helper Rails View Ruby / HTML code
app/helpers Helper Controller Controller
Helper PostsController PostsHelper

Helper
HTML code View

Rails Helper
Helper

Dont repeat yourselfDRY


Good Encapsulation
view

Post
1

<%= @post.content %> %>

<%= simple_format(@post.content) %> %>

<%= truncate(simple_format(@post.content), :lenth => 100) %>

6 - Refactor code

Helper
1

<%= @post.content %> %>

Helper <%= render_post_content(@post) %>


1
2
3

def render_post_content(post)
truncate(simple_format(post.content), :lenth => 100)
end

Partial

http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

118

6 - Refactor code

119

Ch 6.3 partial html


Partial Rails View Partial
View
Helper Rails View Ruby / HTML code
app/helpers Helper Controller Controller
Helper PostsController PostsHelper

Partial
long template | HTML highly duplicated | HTML
independent blocks |

nav/user_info
nav/admin_menu
vendor_js/google_analytics
vendor_js/disqus_js
global/footer

6 - Refactor code

project layout/application.html.erb
1

<%= render :partial => "common/menu" %>

2
3

<div class="container">

4
5

<%= notice_message %>

6
7
8
9
10
11

<div class="content">
<div class="row">
<%= yield %>
<%= yield (:sidebar) %>
</div>

12
13

</div>

14
15
16

<%= render :partial => "common/footer" %>


</div>

17
18
19
20
21
22

<%= render :partial => "common/bootstrap_modal" %>


<%= render :partial => "common/facebook_js" %>
<%= render :partial => "common/google_analytics" %>

23
24

<%= javascript_include_tag "application" %>

25
26

<%= yield :javascripts %>

Partial

http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

120

6 - Refactor code

121

Ch 6.4 scope query


Ch 5.3 index controller

class Account::PostsController < ApplicationController

2
3
4
5

def index
@posts = current_user.posts.order("updated_at DESC")
end

6
7

end
posts.order("updated_at DESC") View

Helper Query

class Account::PostsController < ApplicationController

2
3
4
5

def index
@posts = current_user.posts.recent
end

6
7

end

controller Rails ORM


Scope query
1
2
3

class Post < ActiveRecord::Base


scope :recent, -> { order("updated_at DESC") }
end

6 - Refactor code

Scope

1
2
3
4

class Post < ActiveRecord::Base


scope :recent, -> { order("updated_at DESC") }
scope :published, -> { where(:published => true) }
end

class Account::PostsController < ApplicationController

2
3

before_action :login_required

4
5
6
7

def index
@posts = current_user.posts.published.hot
end

8
9

end

scope

http://edgeguides.rubyonrails.org/active_record_querying.html#scopes

122

7 - Rake db:seed

Rake
rake db db migration
rake Group

rake -T
task rake db:drop ; rake db:create ; rake db:migrate ; rake
db:seed
task rake dev:fake ( namespace : dev,
task fakefake Populator #

Ruby on Rails Rake Tutorial


Whats New in Edge Rails: Database Seeding

http://jasonseifer.com/2010/04/06/rake-tutorial
http://ryandaigle.com/articles/2009/5/13/what-s-new-in-edge-rails-database-seeding

7 - Rake db:seed

124

Ch 7.1 Rake
Rake Ruby MakeRuby Make Ruby Build Tool
Ruby compile Build Tool
Rake Build Tool task Ruby based
Rake Rails task

Rails rake tasks


rake -T Rails project rake tasks ( plugin task

)
1
2
3
4
5
6
7
8
9

rake about
# List versions of all Rails frameworks and the environme\
nt
rake assets:clean
# Remove compiled assets
rake assets:precompile # Compile all the assets named in config.assets.precompile
rake db:create
# Create the database from config/database.yml for the cu\
rrent Rails.env (use db:create:all to create all dbs in the config)
rake db:drop
# Drops the database for the current Rails.env (use db:dr\
op:all to drop all databases)
.....

migration rake dev:build

db migration
migration rake
lib/tasks/dev.rake
1
2
3
4
5

namespace :dev do
desc "Rebuild system"
task :build => ["tmp:clear", "log:clear", "db:drop", "db:create", "db:migrate"]\
task :rebuild => [ "dev:build", "db:seed" ]
end

rake dev:build
1

$ rake dev:build
rake dev:build

7 - Rake db:seed

125

rake db:seed
db/seed.rb

seed.rb rake

1
2
3
4

admin = User.new(:email => "admin@example.org", :password => "123456",


:password_confirmation => "123456")
admin.is_admin = true
admin.save!

5
6
7
8

normal_user = User.new(:email => "user1@example.org", :password => "123456",


:password_confirmation => "123456")
normal_user.save!

9
10

board = Board.create!(:name => "System Announcement")

11
12
13
14
15

post = board.posts.build(:title => "First Post", :content => "This is a demo post\
")
post.user_id = admin.id
post.save!

task rake db:drop ; rake db:create ; rake db:migrate ;


rake db:seed
task rake dev:fake ( namespace : dev,
task fakefake Populator #

8 - deploy VPS

VPS Ruby on Rails production Ruby Enterprise Edition mod_rails Capistrano application

capistrano
cap deploy:setup
cap deploy
cap deploy:rollback
cap deploy:restart

rails-nginx-passenger-ubuntu
AWDR4 deploy

https://github.com/capistrano/capistrano/wiki
https://github.com/jnstq/rails-nginx-passenger-ubuntu
http://pragprog.com/titles/rails4/agile-web-development-with-rails

8 - deploy VPS

127

Ch 8.1 Rails Production


Rails Application
Ubuntu / Debian Ruby Enterprise Editionweb sever
nginx + mod_rails Capistrano recipe deploy

Ruby Ruby

Ruby RubyGemRails
RubyGem

Ruby

Ubuntu / Debian CentOS

Rails Gem
compatible library CentOS package outdate
Ubuntu / Debian package

Best Practice

ImageMagick ( rmagick
gem) MySQL ( mysql2 gem) gem

Step by Step guide https://github.com/rocodev/guides/wiki/


setup-production-development

VPS

enom.com
VPS Linode AWS EC2
micro instance

http://linode.com/
http://aws.amazon.com/ec2/

8 - deploy VPS

128

Ch 8.2 Capistrano
Capistrano 37 signals automate deploy tool Rails Developer

deploy application
Rails php
Rails Application server checkout migration
server memcached search daemon .

deploy

Capistrano Bootstrappets
recipe
config/deploy.rb

https://github.com/capistrano/capistrano/wiki

129

8 - deploy VPS
1

# -*- encoding : utf-8 -*-

2
3
4

raw_config = File.read("config/config.yml")
APP_CONFIG = YAML.load(raw_config)

5
6
7
8

require "./config/boot"
require "bundler/capistrano"
require "rvm-capistrano"

9
10

default_environment["PATH"] = "/opt/ruby/bin:/usr/local/bin:/usr/bin:/bin"

11
12
13
14

set :application, "groupme"


set :repository, "git@github.com:example/#{application}.git"
set :deploy_to, "/home/apps/#{application}"

15
16
17

set :branch, "master"


set :scm, :git

18
19
20

set :user, "apps"


set :group, "apps"

21
22
23
24
25
26
27

set
set
set
set
set
set

:deploy_to, "/home/apps/#{application}"
:runner, "apps"
:deploy_via, :remote_cache
:git_shallow_clone, 1
:use_sudo, false
:rvm_ruby_string, '1.9.3'

28
29
30
31

set :hipchat_token, APP_CONFIG["production"]["hipchat_token"]


set :hipchat_room_name, APP_CONFIG["production"]["hipchat_room_name"]
set :hipchat_announce, false # notify users?

32
33
34
35
36
37

role :web, "groupme.com"


role :app, "groupme.com"
Web` server
role :db, "groupme.com"
ll run

# Your HTTP server, Apache/etc


# This may be the same as your `\
, :primary => true # This is where Rails migrations wi\

38
39
40
41
42

set
set
set
set

:deploy_env, "production"
:rails_env, "production"
:scm_verbose, true
:use_sudo, false

8 - deploy VPS

130

43
44
45

namespace :deploy do

46
47
48
49
50
51

desc "Restart passenger process"


task :restart, :roles => [:web], :except => { :no_release => true } do
run "touch #{current_path}/tmp/restart.txt"
end
end

52
53
54
55
56
57

namespace :my_tasks do
task :symlink, :roles => [:web] do
run "mkdir -p #{deploy_to}/shared/log"
run "mkdir -p #{deploy_to}/shared/pids"

58
59
60
61
62
63
64

symlink_hash = {
"#{shared_path}/config/database.yml"
=> "#{release_path}/config/database.\
yml",
"#{shared_path}/config/s3.yml"
=> "#{release_path}/config/s3.yml",
"#{shared_path}/uploads"
=> "#{release_path}/public/uploads",
}

65
66
67
68
69

symlink_hash.each do |source, target|


run "ln -sf #{source} #{target}"
end
end

70
71

end

72
73
74
75
76
77
78
79
80

namespace :remote_rake do
desc "Run a task on remote servers, ex: cap staging rake:invoke task=cache:clea\
r"
task :invoke do
run "cd #{deploy_to}/current; RAILS_ENV=#{rails_env} bundle exec rake #{ENV['\
task']}"
end
end

81
82

after "deploy:finalize_update", "my_tasks:symlink"

8 - deploy VPS

131

Ch 8.3 Capistrano
cap deploy:setup
Capistrano Capistrano

cap deploy
Deploy

cap deploy:migrate
migration

cap deploy:rollback
deploy

cap deploy:restart
application

8 - deploy VPS

Ch 8.4 Deploy with Rails 4


Rails 4 Server Asset Compile

Rails 3
config/production.rb
1
2

config.serve_static_assets = false
config.assets.compile = false

Rails 4
Rails 4
1
2
3
4
5
6

config.serve_static_assets = true
config.assets.compile = true
config.assets.compress = true
config.assets.configure do |env|
env.logger = Rails.logger
end

132

Asset

Pipeline

Rails 3.1 Asset Asset Pipeline


(minify) (compress) Javascript / CSS
CoffeeScript / SASS / ERB assets ( stylesheets / javascripts /
images )

SCSS
SCSS Asset CSS
1
2

.content{ margin: 2em 0;}


.content h1{ font-size: 2em;}

SCSS
1
2
3
4

.content{
margin: 2em 0;
h1{font-size: 2em;}
}

CSS

Asset Pipeline

134

SCSS mixin
protyping

+
CSS

SCSS style
1
2
3
4
5
6

$border-color: #3bbfce;
$link-color: #3bbfcf'
.content{
border-color: $border-color;
a{ color: $link-color; }
}

1
2

.content{ border-color: #3bbfce; }


.content a{color: #3bbfcf; }

Compass
SCSS Compass Framework mixin Developer

1
2
3
4
5
6
7
8

#border-radius {
-moz-border-radius: 25px;
-webkit-border-radius: 25px;
-o-border-radius: 25px;
-ms-border-radius: 25px;
-khtml-border-radius: 25px;
border-radius: 25px;
}

Compass
http://compass-style.org/

Asset Pipeline
1

#border-radius {

@include border-radius(25px); }

135

Asset Pipeline

136

CoffeeScript
Asset Pipeline CoffeeScriptCoffeeScript
CoffeeScript JavaScript Ruby Python
Plurk amix post CoffeeScript: The beautiful way to write
Javascript CoffeeScript
JavaScript
JavaScript
JavaScript functional language
OOP prototype-based JavaScript dynamic language Lisp
C/Java C/Java
Java Java
functional & dynamic laungaue Ruby / Python C / Java
syntax
CoffeeScript JavaScript

CoffeeScript : The Good Part


CoffeeScript JavaScript Good Parts JavaScript

JavaScript
CoffeeScript JavaScript var
Protected code
CoffeeScript function JavaScript anonymous function:
function(){}(); function

-> indent() function

JavaScript function(){}();
.
CoffeeScript JavaScript
http://jashkenas.github.com/coffee-script/
http://amix.dk/blog/post/19612

Asset Pipeline

137

syntax error

JavaScript syntax error


CoffeeScript compile syntax error

Javascript JavaScript
class
JavaScript OOP prototypefunction
Object
CoffeeScript
1
2

class Animal
constructor: (@name) ->

3
4
5

move: (meters) ->


alert @name + " moved #{meters}m."

6
7
8
9
10

class Snake extends Animal


move: ->
alert "Slithering..."
super 5

11
12
13
14
15

class Horse extends Animal


move: ->
alert "Galloping..."
super 45

16
17
18

sam = new Snake "Sammy the Python"


tom = new Horse "Tommy the Palomino"

19
20
21

sam.move()
tom.move()

JavaScript OO

Asset Pipeline

138

Asset Pipeline
Asset Pipeline assets assets
app/assets
lib/assets
vendor/assets
assets application.css / application.js require

library

3rd party vendor assets application.css application.js require

1
2

//= require jquery


//= require bootstrap

app/assets
Rails 3.1.x rails g controler posts assets/styelsheets/ assets/javascripts/ scss coffeescript app/assets
assets
lib/assets
lib library LIBRARY assets
library mixin bootstrap lib/

vendor/assets
verdor assets :
jquery.*.js
fanfanfan icons
tinymce / ckeditor

Asset Pipeline

139

Rails Asset Gem


Asset Pipleline Gem 3rd party CSS
Framework Rails
Fontawesome icon Rails gem https://github.com/
bokmann/font-awesome-rails
application.css
1

//= require font-awesome

framwork COPY

Asset Pipeline

140

Rails 4 with Asset Pipeline


Rails 4 asset pipeline Rails 3

Rails 3
Rails 3 asset gem asset group
1
2
3
4
5
6

group
gem
gem
gem
gem
end

:assets do
'sass-rails',
'~> 3.2.3'
'coffee-rails', '~> 3.2.1'
'uglifier', '>= 1.0.3'
"compass-rails"

Rails 4
Rails 4 gem asset group compass
1.1.2 gem compass
1
2
3
4

gem
gem
gem
gem

'uglifier', '>= 1.3.0'


'coffee-rails', '~> 4.0.0'
'sass-rails', '~> 4.0.0'
"compass-rails", "~> 1.1.2"

Asset Pipeline

141

rvm
pow
CRUD
RESTFUL
RESTFUL
Devise)
Scope
Helper
Partial
Capistrano
Asset Pipeline

chapter git branch Github

Groupme admin Github


Rails API

Ruby Rails /

Code School Try Ruby


Code School Try Git
Code School Git Real
Peepcode Meet Command Line
Peepcode Advanced Command Line
Zed Shaw Learn Ruby The Hard Way

Learning Rails
Application
Code School Rails for Zombies Redux
Code School Rails for Zombies 2

CodeSchool Try jQuery


CodeSchool JavaScript Road Trip Part 1
CodeSchool JavaScript Road Trip Part 1
CodeSchool CSS Cross-Country
Codecademy Javascripts

http://www.codeschool.com/courses/try-ruby
http://www.codeschool.com/courses/try-git
http://www.codeschool.com/courses/git-real
https://peepcode.com/screencasts
https://peepcode.com/products/advanced-command-line
http://ruby.learncodethehardway.org/
http://www.codeschool.com/courses/rails-for-zombies-redux
http://www.codeschool.com/courses/rails-for-zombies-2
https://www.codeschool.com/courses/try-jquery
https://www.codeschool.com/courses/javascript-road-trip-part-1
https://www.codeschool.com/courses/javascript-road-trip-part-2
http://www.codeschool.com/courses/css-cross-country
http://www.codecademy.com/zh/tracks/javascript

Ruby on Rails

TDD
Michael Hartl Rails Turtorial
Ryan Bigg Rails in Action 4
UT on Rails is also a excellent learning material
Schneems UT on Rails

Testing
Code School Rails testing for zombies
Code School Testing with Rspec
Noel Rappin Rails Test Prescriptions: Keeping Your Application Healthy
Thougutbot Learn Test-Driven Development using RSpec and Capybara.

Code School Jounry into Mobile


Code School The Anatomy of Backbone
Code School CoffeeScript
Code School Assembling Sass
Code School Assembling Sass Part2

http://ruby.railstutorial.org/
http://www.manning.com/bigg2/
http://schneems.com/ut-rails
http://www.codeschool.com/courses/rails-testing-for-zombies
http://www.codeschool.com/courses/testing-with-rspec
http://pragprog.com/book/nrtest/rails-test-prescriptions
https://learn.thoughtbot.com/workshops/18-test-driven-rails
http://www.codeschool.com/courses/journey-into-mobile
http://www.codeschool.com/courses/anatomy-of-backbonejs
http://www.codeschool.com/courses/coffeescript
http://www.codeschool.com/courses/assembling-sass
http://www.codeschool.com/courses/assembling-sass

143

144

Ruby / Rails code

Codschool Rails Best Practices


Chad Pytel / Tammer Saleh : Rails Antipattern
John Athayde / Bruce Williamsp The Rails View: Create a Beautiful and Maintainable User
Experience
Eric Davis Refacotoring Redmine
Code Climate 7 Patterns to Refactor Fat ActiveRecord Models

Ruby code

Code School Code Ruby Bits


Code School Code Ruby Bits Part 2
David A. Black The Well-Grounded Rubyist
Russ Olsen Eloquent Ruby
Avdi Grimm Confident Ruby
Avdi Grimm Exceptional Ruby
Stefan Kaes Writing Efficient Ruby Code (Digital Short Cut)

Podcast / Journal of writing better Ruby/Rails code


Ruby Tapas
Destroy All Software
Practicing Ruby
http://www.codeschool.com/courses/rails-best-practices
http://railsantipatterns.com/
http://pragprog.com/book/warv/the-rails-view
http://www.refactoringredmine.com/
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
http://www.codeschool.com/courses/ruby-bits
http://www.codeschool.com/courses/ruby-bits-part-2
http://www.manning.com/black2/
http://www.amazon.com/Eloquent-Ruby-Addison-Wesley-Professional/dp/0321584104
http://devblog.avdi.org/2012/06/05/confident-ruby-beta/
http://exceptionalruby.com/
http://www.informit.com/store/writing-efficient-ruby-code-digital-short-cut-9780321540034
http://devblog.avdi.org/rubytapas/
https://www.destroyallsoftware.com/screencasts
https://practicingruby.com/

145

Object-oriend Design in Ruby on Rails

thoughtbot Ruby Science


Avdi Grimm Object on Rails
Russ Olsen Design Patterns in Ruby
Jay fields Refacoting : Ruby Edition
Sandi Metz Practical Object-Oriented Design in Ruby: An Agile Primer

Rails

Jos Valim Crafting Rails Applications: Expert Practices for Everyday Rails Development
Marc-Andr Cournoyer Owning Rails: The Rails Online Master Class
Railscast Rails Initialization Walkthrough
Railscast Rails Middleware Walkthrough
Railscast Rack App from Scratch
Railscast Rails Modularity
Railscast Hacking with Arel
Railscast Authorization from Scratch Part 1
Railscast Authorization from Scratch Part 2
Railscast Action Controller Walkthrough
Railscast Action View Walkthrough

https://learn.thoughtbot.com/products/13
http://objectsonrails.com/
http://www.amazon.com/Design-Patterns-Ruby-Russ-Olsen/dp/0321490452
http://www.amazon.com/Design-Patterns-Ruby-Russ-Olsen/dp/0321490452
http://www.amazon.com/dp/0321721330
http://pragprog.com/book/jvrails/crafting-rails-applications
http://owningrails.com/
http://railscasts.com/episodes/299-rails-initialization-walkthrough
http://railscasts.com/episodes/319-rails-middleware-walkthrough
http://railscasts.com/episodes/317-rack-app-from-scratch
http://railscasts.com/episodes/349-rails-modularity
http://railscasts.com/episodes/355-hacking-with-arel
http://railscasts.com/episodes/385-authorization-from-scratch-part-1
http://railscasts.com/episodes/386-authorization-from-scratch-part-2
http://railscasts.com/episodes/395-action-controller-walkthrough
http://railscasts.com/episodes/397-action-view-walkthrough


Resources of latest Ruby

Ruby5
Ruby Weekly
Ruby Inside
RubyFlow
RubyRogues
Thoughtbot Podcast
Railscast
Confreaks

http://ruby5.envylabs.com/
http://rubyweekly.com/
http://www.rubyinside.com/
http://www.rubyflow.com/
http://rubyrogues.com/
http://learn.thoughtbot.com/podcast
http://railscasts.com/
http://www.confreaks.com/

You might also like