Rails 2015 秋季班  第三週課堂複習及作業筆記


這週上課的內容和作業的概念和實作內容都是以前沒碰過的，因此週末來整個重新做一次，邊查資料、留個筆記未來可以參照。

# 前置作業

需要注意 ruby runtime 的版本，可以對照一下 [Heroku 支援了哪些版本](https://devcenter.heroku.com/articles/ruby-support#supported-runtimes) ，調整 Gemfile 裡面指定的 ruby 版本。

# 寄信

## ActionMailer 

[ActionMailer Basics (官方)](http://guides.rubyonrails.org/action_mailer_basics.html)
[Ruby on Rails 實戰聖經 - ActionMailer - E-mail 發送](https://ihower.tw/rails4/actionmailer.html)

寄信方式 
- `:test`: 寄信方式設成 `:test` 代表並不會實際寄信出去，而是存在 `ActionMailer::Base.deliveries` 陣列中方便做自動化測試。
- `:sendmail`: 使用伺服器的 `/usr/bin/sendmail` 程式來寄信
- `:smtp`: 最推薦的寄信方式

## 新增 Mailer

在 Rails 專案生成後，不會有 `app/mailers/application_mailer.rb` 這個檔案存在，當第一次執行 mailer 產生器的時候，才會一併建立出來。

``` shell
rails g mailer OrderMailer
```

接著就會有以下 prompt ，告知有什麼檔案生成：

```
      create  app/mailers/order_mailer.rb
      create  app/mailers/application_mailer.rb
      invoke  erb
      create    app/views/order_mailer
      create    app/views/layouts/mailer.text.erb
      create    app/views/layouts/mailer.html.erb
      invoke  test_unit
      create    test/mailers/order_mailer_test.rb
      create    test/mailers/previews/order_mailer_preview.rb
```
### 樣板檔案

可以看到有生成兩個樣板檔： `mailer.text.erb` 和 `mailer.html.erb` 
從檔名後綴可以看出來， .text 就是純文字的信件、 .html 就會是有包含 HTML 內容的信件。

當兩個樣板檔案都存在的時候，寄信時則會將兩者的內容都帶入信件中

### 設定預設寄件人

在 ActionMailer 裡面，使用 `default :from` 可以設定預設寄件人信箱：

``` ruby
class ApplicationMailer < ActionMailer::Base
    default :from => "foobar@example.org"
    layout "mailer"
end

```

### 新增寄信的 method

打開 `app/mailers/order_mailer.rb` 來新增要寄信的 method 準備資料以及完成寄信的動作。新增完 method 後，在新增對應的樣板檔案，來呈現信件的內容。

因此，這個階段會做：

- 在 order_mailer.rb 中新增 `notify_order_placed(order)` method
- 手動新增 `app/views/order_mailer/notify_order_placed.html.erb` 這個樣板檔案。

因為我們要寄送的信件含有 HTML ，因此我們使用了 .html 這個後綴。

### 觸發寄信

ActionMailer 有個 method: [`deliver!`](http://apidock.com/rails/v2.3.8/ActionMailer/Base/deliver%21) ，像是這樣即可：

```
OrderMailer.notify_order_placed(Order.last).deliver!
```

也可以在 `rails c` 的環境下執行這個指令也可以寄信

### 預覽信件外觀

加入這個 gem 就可以預覽寄出去的信件

- [letter_opener](https://github.com/ryanb/letter_opener)

加入 Gemfile 、 bundle install 過後，在環境設定檔裡面加入以下這行

``` ruby:config/environments/development.rb
config.action_mailer.delivery_method = :letter_opener
```

重啟 rails s 之後，在寄信時，就不會真的祭出，而是跳出信件的預覽圖

[參考](https://github.com/ryanb/letter_opener#rails-setup)

## 註冊 Mailgun

[Mailgun 官網](https://mailgun.com)
[Rails 的環境變數](http://railsapps.github.io/rails-environment-variables.html)

### 設定 production 環境

這次是要 deploy 到 heroku 上面，所以要用環境變數的方式設定這些參數

``` ruby:config/environments/production.rb
...
  config.action_mailer.default_url_options = { host: 'yourdomain.com' }
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    port:           587,
    address:        "smtp.mailgun.org",
    user_name:      ENV["MAILGUN_USERNAME"],
    password:       ENV["MAILGUN_PASSWORD"],
    domain:         ENV["MAILGUN_DOMAIN"],
    authentication: :plain,
  }
end
```

#### 在 Heroku 設定 mailgun 要用到的環境變數

Deploy 到 Heroku 的機器上去之後，記得要設定在上面有設定到的三個 ENV 變數。

在 Heroku 的 app 設定頁面，選定 "Settings 頁籤" ，在 "Config Variables" 的 section, 就可以設定變數，點擊右邊的 "Reveal Config Vars" 按鈕就可以展開環境變數的列表了。

接著只要填入 `MAILGUN_USERNAME`, `MAILGUN_PASSWORD`, `MAILGUN_DOMAIN` 這三組 key-value 即可。

這三個值分別是對應到 mailgun 上 **Domian Information** 頁面的 `Default SMTP Login`, `Default SMTP Password` 和 Login 帳號 *@* 後的那段 domain 就好。

# 把使用者上傳的圖片丟到 S3

恩 ... 因為 heroku 不能存使用者自己上傳的圖片，所以這時候就要接 S3 來負責放圖片了。

怎麼申請及設定 S3: [Heroku Dev Center - Using AWS S3 to Store Static Assets and File Uploads](https://devcenter.heroku.com/articles/s3)
怎麼在 Rails app 使用 S3: 
[Heroku Dev Center - Direct to S3 Image Uploads in Rails](https://devcenter.heroku.com/articles/direct-to-s3-image-uploads-in-rails)

## 保存 S3 的密鑰

在新增一組 key 的時候，請 **務必** 把密鑰資料下載下來保留好，在生成之後，管理平台上就不會有地方可以看秘鑰。如果沒有保存好的話，就只能刪掉 Key 再重新設定。

## 裝 fog 來介接 S3


`fog` 可以說是一個通用的接口，可以幫忙簡單快速的設定好不同雲端服務，如 S3, GCS 等。

[fog.io](http://fog.io/storage/)
[怎麼用 fog 連接 S3](http://fog.io/storage/#using-amazon-s3-and-fog)


``` ruby:Gemfile
...
gem "fog"
...
```

裝完之後跑 bundle install 就可以了

### 新增 CarrierWave 設定檔

並透過 fog 來設定 S3：

先新增這一個檔案，來初始化設定 CarrierWave

``` ruby:config/initializers/carrierwave.rb
CarrierWave.configure do |config|
	if Rails.env.production?
		config.fog_credentials = {
			provider: "AWS",
			aws_access_key_id: ENV["AWS_ACCESS_KEY_ID"],
			aws_secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
			region: ENV["AWS_REGION"] # 設定成 bucket 所在的位置，每個人可能會不同
		}
		config.fog_directory = ENV["AWS_BUCKET"]
		# 最後要把儲存位置指定成 :fog, carrierwave 才會去用 fog 的設定
		config.storage :fog
	else
		config.storage :file
	end
end

```

其中的環境變數當然也要在 Heroku 上面設定好。

#### 區域代號

`region` 的參數也需要根據選擇的機器設定正確的代號，可以參考[官方的文件](http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region) ，如果設定錯誤，則會跳出 SocketError

#### 移除原先設定

接著，在原本有的 image uploader class 中的儲存設定刪掉即可：

``` diff:app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
   ...
-  storage :file
   ...
end
```