Skip to content

Commit

Permalink
fix ch-1 error
Browse files Browse the repository at this point in the history
  • Loading branch information
xdite committed Nov 10, 2013
1 parent 9a3d7d5 commit 5a18da2
Show file tree
Hide file tree
Showing 14 changed files with 206 additions and 61 deletions.
1 change: 1 addition & 0 deletions manuscript/Book.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ chapter-05-3.txt
chapter-05-4.txt
chapter-06.txt
chapter-06-1.txt
chapter-06-2.txt
chapter-07.txt
chapter-07-1.txt
chapter-07-2.txt
Expand Down
46 changes: 33 additions & 13 deletions manuscript/chapter-00.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@

### 作業解答

安裝 Rails 4.0.0 rc
安裝 Rails 4.0.0

~~~~~~~
gem install rails --version 4.0.0.rc1
gem install rails --version 4.0.0
~~~~~~~

打開 Terminal,在 ~/projects 下輸入指令,建立一個叫做 groupmy 的 Rails 專案
Expand Down Expand Up @@ -55,29 +55,49 @@ Bundler 依據 `Gemfile` 這個檔案安裝以及判斷套件相依性。你的
source 'https://rubygems.org'

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

# Use sqlite3 as the database for Active Record
gem 'sqlite3'

# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem 'sass-rails', '~> 4.0.0.beta1'
gem 'coffee-rails', '~> 4.0.0.beta1'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 4.0.0'

# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'

gem 'uglifier', '>= 1.0.3'
end
# Use CoffeeScript for .js.coffee assets and views
gem 'coffee-rails', '~> 4.0.0'

# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby

# Use jquery as the JavaScript library
gem 'jquery-rails'

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

# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 1.0.1'
gem 'jbuilder', '~> 1.2'

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

# Use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'

# Use unicorn as the app server
# gem 'unicorn'

# Use Capistrano for deployment
# gem 'capistrano', group: :development

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

~~~~~~~~

I>## 常用 Bundler 指令:
Expand Down
8 changes: 4 additions & 4 deletions manuscript/chapter-01-00-ext-01.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@ $ rake db:migrate

### What is 「db migration」?

在傳統開發 web 應用程式的流程中,開發者多半是使用 phpmyadmin 開設 db 欄位。因此在學習 Rails 之初,新手往往會提出一個好問題:為何在 Rails 內需要撰寫 db migration 檔?
在傳統開發 web 應用程式的流程中,開發者多半是使用 phpmyadmin 開設 db 欄位。

我來回答這個問題:理由是 db migration 是一個經過實證的 best practices。當多人同時在一個 project 內工作時,縱然程式碼可以受到版本控制,但是 db schema 卻可能是無法透過版本控制的一個隱憂。
因此在學習 Rails 之初,剛入門的開發者往往會提出一個質疑:為何必須撰寫 db migration 檔去生欄位,而不是使用傳的 phpmyadmin?

當多人都可以自由的刪改 db 欄位不受監督時,程式碼就無法與 db 欄位有著一致性,將會有出錯的可能性
理由是 db migration 是一個經過實證的最佳解。當多人同時在一個 project 內工作時,縱然程式碼可以受到版本控制,但是 db schema 卻可能是無法透過版本控制的一個隱憂

為了 automation 以及也能對 db schema 做版本控制,所以才有了 db migration file 的設計。
當多人都可以自由的刪改 db 欄位不受監督時,程式碼就很難與 db 欄位有著一致性,將會有出錯的可能性。為了 automation 以及也能對 db schema 做版本控制,所以才有了 db migration file 的設計。


### 小結
Expand Down
2 changes: 1 addition & 1 deletion manuscript/chapter-01-00-ext-02.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#### 安裝 boostrappers

~~~~~~~~
gem install bootstrappers -v=4.0.rc1
gem install bootstrappers -v=4.2.0.5
~~~~~~~~

#### 用 boostrappers 建立新專案
Expand Down
77 changes: 76 additions & 1 deletion manuscript/chapter-01-1-4-1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,80 @@

## Ch 1.1.4 (補充) Strong Parameters

TODO
還記得 2012 年初開發圈曾經騰動一時的: [Github 被入侵事件](http://blog.xdite.net/posts/2012/03/05/github-hacked-rails-security/) 嗎?

事件的起因是因為 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 的設計頓時就不夠解決問題了…

{::pagebreak :/}

### 癥結點:欄位核准與否應該由 controller 管理,而非 model

大家戰了一陣子,終於收斂出一個結論。一切的癥結點在於之前的設計想法都走錯了方向,欄位核准與否應該由 controller 決定。因為「流程需求」本來就應該作在 controller 裡面。新的 solution [strong_parameters](https://github.com/rails/strong_parameters/) 就是這個解法。

在當時:Rails 之父 DHH 提供了他的最佳實務:


~~~~~~~~

class PostsController < ActionController::Base
def create
Post.create(post_params)
end

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

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

使用 slice 去把真正需要的部分切出來,所以就算 hacker 打算送其他 parameter 也會被過濾掉(不會有 exception)。

{::pagebreak :/}

### Strong Parameters 的作法

而 strong_parameters 的作法是必須過一段 permit,允許欄位。如果送不允許的欄位進來,會 throw exception。

~~~~~~~~
class PeopleController < ActionController::Base
def update
person.update_attributes!(person_params)
redirect_to :back
end

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

### 進階 Strong Parameters

當然,每一段 controller 都要來上這麼一段,有時候也挺煩人的。Railscast 也整理了一些[進階招數](http://railscasts.com/episodes/371-strong-parameters):

* Nested Attributes
* Orgngized to Class
37 changes: 20 additions & 17 deletions manuscript/chapter-01-1-4.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,19 @@
在 `app/controllers/groups_controller.rb` 加入 `create` 這個 action

~~~~~~~~

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

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




private


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

~~~~~~~~

不過此時,我們發現好像少了一種實際狀況?如果一個 Group 沒有 title,應該算是不合法的 group 吧?
Expand All @@ -47,13 +44,19 @@ end
~~~~~~~~

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

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



private


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

~~~~~~~~
Expand Down
2 changes: 1 addition & 1 deletion manuscript/chapter-04-1-2.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{::pagebreak :/}

## 4.1.2 model method 與 after create
## Ch 4.1.2 model method 與 after create

實作以下 method 在 User model 內

Expand Down
2 changes: 1 addition & 1 deletion manuscript/chapter-04-1-3.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{::pagebreak :/}


## 4.1.3 join 與 quit action
## Ch 4.1.3 join 與 quit action

在 Groups Controller 加入以下這兩個 action

Expand Down
2 changes: 1 addition & 1 deletion manuscript/chapter-04-1.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{::pagebreak :/}

## 4.1 使用者必須要是這個社團的成員才能發表文章
## Ch 4.1 使用者必須要是這個社團的成員才能發表文章

在第 2 章、第 3 章我們完成了 Group 與 Post 的新增與管理。在這一章我們要挑戰一點進階的課題。

Expand Down
4 changes: 2 additions & 2 deletions manuscript/chapter-05-4.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
(0.2ms) SELECT COUNT(*) FROM `posts` WHERE `posts`.`group_id` = 1
~~~~~~~~

如果你有開發過網頁程式 application 的經驗,在迴圈裡面跑 count 對效能是相當傷的。實務上我們相當不建議這麼做。
如果你有開發過網頁程式 application 的經驗,就知道在迴圈裡面跑 count 對效能是相當傷的。實務上我們相當不建議這麼做。

那麼,這一段要怎麼改善呢?比較直觀的想法,就是在 groups 這個 table 再開一欄叫作 `posts_count` 的欄位。然後在
那麼這一段程式碼要怎麼改善呢?比較直觀的想法,就是在 groups 這個 table 再開一欄叫作 `posts_count` 的欄位。然後在
create action 裡面對 `posts_count` +1

~~~~~~~~
Expand Down
52 changes: 52 additions & 0 deletions manuscript/chapter-06-1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{::pagebreak :/}

## 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` 時,有個困擾。我們在輸入框輸入

~~~~~
a
b
c
~~~~~

但在系統輸出的時候,HTML 並不會自動斷行,會顯示成

~~~~~
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 ) %>`
5 changes: 5 additions & 0 deletions manuscript/chapter-06-2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Ch 6.2 自己撰寫的 helper 包裝 html

## Ch 6.3 使用 partial 整理 html

## Ch 6.4 使用 scope 整理 query
8 changes: 2 additions & 6 deletions manuscript/chapter-06.txt
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
# 練習作業 6 - Refactor code


## 作業目標
### 作業目標

* 使用系統 helper / 自己撰寫的 helper 包裝 html
* 使用 partial 整理 html
* 使用 scope 整理 query


## 練習主題
### 練習主題

* 練習使用內建 helper
* 練習拆 partial
* 練習使用 scope 機制包裝不同 condition 的 SQL query




## 作業解答

TODO
Loading

0 comments on commit 5a18da2

Please sign in to comment.